diff options
Diffstat (limited to 'gcc')
132 files changed, 4493 insertions, 515 deletions
diff --git a/gcc/analyzer/checker-event.cc b/gcc/analyzer/checker-event.cc index 958cdbf..e041778 100644 --- a/gcc/analyzer/checker-event.cc +++ b/gcc/analyzer/checker-event.cc @@ -112,7 +112,8 @@ checker_event::checker_event (enum event_kind kind, m_original_depth (loc_info.m_depth), m_effective_depth (loc_info.m_depth), m_pending_diagnostic (NULL), m_emission_id (), - m_logical_loc (loc_info.m_fndecl) + m_logical_loc + (tree_logical_location_manager::key_from_tree (loc_info.m_fndecl)) { /* Update effective fndecl and depth if inlining has been recorded. */ if (flag_analyzer_undo_inlining) @@ -122,7 +123,8 @@ checker_event::checker_event (enum event_kind kind, { m_effective_fndecl = info.get_inner_fndecl (); m_effective_depth += info.get_extra_frames (); - m_logical_loc = tree_logical_location (m_effective_fndecl); + m_logical_loc + = tree_logical_location_manager::key_from_tree (m_effective_fndecl); } } } @@ -141,7 +143,8 @@ checker_event::get_meaning () const void checker_event:: -maybe_add_sarif_properties (sarif_object &thread_flow_loc_obj) const +maybe_add_sarif_properties (sarif_builder &builder, + sarif_object &thread_flow_loc_obj) const { sarif_property_bag &props = thread_flow_loc_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/checker_event/" @@ -150,12 +153,11 @@ maybe_add_sarif_properties (sarif_object &thread_flow_loc_obj) const props.set_string (PROPERTY_PREFIX "kind", event_kind_to_string (m_kind)); if (m_original_fndecl != m_effective_fndecl) - { - tree_logical_location logical_loc (m_original_fndecl); - props.set<sarif_logical_location> - (PROPERTY_PREFIX "original_fndecl", - make_sarif_logical_location_object (logical_loc)); - } + props.set_logical_location + (PROPERTY_PREFIX "original_fndecl", + builder, + tree_logical_location_manager::key_from_tree (m_original_fndecl)); + if (m_original_depth != m_effective_depth) props.set_integer (PROPERTY_PREFIX "original_depth", m_original_depth); #undef PROPERTY_PREFIX @@ -502,10 +504,11 @@ state_change_event::get_meaning () const for superedge_event. */ void -superedge_event::maybe_add_sarif_properties (sarif_object &thread_flow_loc_obj) +superedge_event::maybe_add_sarif_properties (sarif_builder &builder, + sarif_object &thread_flow_loc_obj) const { - checker_event::maybe_add_sarif_properties (thread_flow_loc_obj); + checker_event::maybe_add_sarif_properties (builder, thread_flow_loc_obj); sarif_property_bag &props = thread_flow_loc_obj.get_or_create_properties (); #define PROPERTY_PREFIX "gcc/analyzer/superedge_event/" if (m_sedge) diff --git a/gcc/analyzer/checker-event.h b/gcc/analyzer/checker-event.h index f3ab899..2f26b8d 100644 --- a/gcc/analyzer/checker-event.h +++ b/gcc/analyzer/checker-event.h @@ -100,12 +100,9 @@ public: location_t get_location () const final override { return m_loc; } int get_stack_depth () const final override { return m_effective_depth; } - const logical_location *get_logical_location () const final override + logical_location get_logical_location () const final override { - if (m_effective_fndecl) - return &m_logical_loc; - else - return NULL; + return m_logical_loc; } meaning get_meaning () const override; bool connect_to_next_event_p () const override { return false; } @@ -115,7 +112,8 @@ public: } void - maybe_add_sarif_properties (sarif_object &thread_flow_loc_obj) const override; + maybe_add_sarif_properties (sarif_builder &, + sarif_object &thread_flow_loc_obj) const override; /* Additional functionality. */ tree get_fndecl () const { return m_effective_fndecl; } @@ -154,7 +152,7 @@ protected: int m_effective_depth; pending_diagnostic *m_pending_diagnostic; diagnostic_event_id_t m_emission_id; // only set once all pruning has occurred - tree_logical_location m_logical_loc; + logical_location m_logical_loc; }; /* A concrete event subclass for a purely textual event, for use in @@ -391,7 +389,8 @@ public: class superedge_event : public checker_event { public: - void maybe_add_sarif_properties (sarif_object &thread_flow_loc_obj) + void maybe_add_sarif_properties (sarif_builder &, + sarif_object &thread_flow_loc_obj) const override; /* Mark this edge event as being either an interprocedural call or diff --git a/gcc/analyzer/checker-path.h b/gcc/analyzer/checker-path.h index dfc782d..80c975c 100644 --- a/gcc/analyzer/checker-path.h +++ b/gcc/analyzer/checker-path.h @@ -31,8 +31,9 @@ namespace ana { class checker_path : public diagnostic_path { public: - checker_path (logger *logger) - : diagnostic_path (), + checker_path (const logical_location_manager &logical_loc_mgr, + logger *logger) + : diagnostic_path (logical_loc_mgr), m_thread ("main"), m_logger (logger) {} diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc index 7575b16..e5d1a25 100644 --- a/gcc/analyzer/diagnostic-manager.cc +++ b/gcc/analyzer/diagnostic-manager.cc @@ -1580,7 +1580,8 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg, /* This is the diagnostic_path subclass that will be built for the diagnostic. */ - checker_path emission_path (get_logger ()); + checker_path emission_path (get_logical_location_manager (), + get_logger ()); /* Populate emission_path with a full description of EPATH. */ build_emission_path (pb, *epath, &emission_path); @@ -1656,6 +1657,15 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg, } } +const logical_location_manager & +diagnostic_manager::get_logical_location_manager () const +{ + gcc_assert (global_dc); + auto mgr = global_dc->get_logical_location_manager (); + gcc_assert (mgr); + return *mgr; +} + /* Emit a "path" of events to EMISSION_PATH describing the exploded path EPATH within EG. */ diff --git a/gcc/analyzer/diagnostic-manager.h b/gcc/analyzer/diagnostic-manager.h index b62fc7a..aa0bd79 100644 --- a/gcc/analyzer/diagnostic-manager.h +++ b/gcc/analyzer/diagnostic-manager.h @@ -191,6 +191,9 @@ public: } private: + const logical_location_manager & + get_logical_location_manager () const; + void build_emission_path (const path_builder &pb, const exploded_path &epath, checker_path *emission_path) const; diff --git a/gcc/cgraph.h b/gcc/cgraph.h index f7b67ed..f4ee29e 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -965,15 +965,19 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node If the new node is being inlined into another one, NEW_INLINED_TO should be the outline function the new one is (even indirectly) inlined to. All hooks will see this in node's inlined_to, when invoked. - Can be NULL if the node is not inlined. SUFFIX is string that is appended - to the original name. */ + Should be NULL if the node is not inlined. + + SUFFIX is string that is appended to the original name, it should only be + NULL if NEW_INLINED_TO is not NULL or if the clone being created is + temporary and a record about it should not be added into the ipa-clones + dump file. */ cgraph_node *create_clone (tree decl, profile_count count, bool update_original, vec<cgraph_edge *> redirect_callers, bool call_duplication_hook, cgraph_node *new_inlined_to, ipa_param_adjustments *param_adjustments, - const char *suffix = NULL); + const char *suffix); /* Create callgraph node clone with new declaration. The actual body will be copied later at compilation stage. The name of the new clone will be @@ -1020,11 +1024,12 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node TREE_MAP is a mapping of tree nodes we want to replace with new ones (according to results of prior analysis). - If non-NULL ARGS_TO_SKIP determine function parameters to remove - from new version. - If SKIP_RETURN is true, the new version will return void. + If non-NULL PARAM_ADJUSTMENTS determine how function formal parameters + should be modified in the new version and if it should return void. If non-NULL BLOCK_TO_COPY determine what basic blocks to copy. If non_NULL NEW_ENTRY determine new entry BB of the clone. + SUFFIX is a string that will be used to create a new name for the new + function. If TARGET_ATTRIBUTES is non-null, when creating a new declaration, add the attributes to DECL_ATTRIBUTES. And call valid_attribute_p @@ -1039,7 +1044,7 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node (vec<cgraph_edge *> redirect_callers, vec<ipa_replace_map *, va_gc> *tree_map, ipa_param_adjustments *param_adjustments, - bitmap bbs_to_copy, basic_block new_entry_block, const char *clone_name, + bitmap bbs_to_copy, basic_block new_entry_block, const char *suffix, tree target_attributes = NULL_TREE, bool version_decl = true); /* Insert a new cgraph_function_version_info node into cgraph_fnver_htab diff --git a/gcc/cgraphclones.cc b/gcc/cgraphclones.cc index e6223fa..b45ac49 100644 --- a/gcc/cgraphclones.cc +++ b/gcc/cgraphclones.cc @@ -307,12 +307,20 @@ cgraph_node::expand_all_artificial_thunks () e = e->next_caller; } +/* Dump information about creation of a call graph node clone to the dump file + created by the -fdump-ipa-clones option. ORIGINAL is the function being + cloned, CLONE is the new clone. SUFFIX is a string that helps identify the + reason for cloning, often it is the suffix used by a particular IPA pass to + create unique function names. SUFFIX can be NULL and in that case the + dumping will not take place, which must be the case only for helper clones + which will never be emitted to the output. */ + void dump_callgraph_transformation (const cgraph_node *original, const cgraph_node *clone, const char *suffix) { - if (symtab->ipa_clones_dump_file) + if (suffix && symtab->ipa_clones_dump_file) { fprintf (symtab->ipa_clones_dump_file, "Callgraph clone;%s;%d;%s;%d;%d;%s;%d;%s;%d;%d;%s\n", @@ -358,9 +366,14 @@ localize_profile (cgraph_node *n) If the new node is being inlined into another one, NEW_INLINED_TO should be the outline function the new one is (even indirectly) inlined to. All hooks - will see this in node's inlined_to, when invoked. Can be NULL if the + will see this in node's inlined_to, when invoked. Should be NULL if the node is not inlined. + SUFFIX is string that is appended to the original name, it should only be + NULL if NEW_INLINED_TO is not NULL or if the clone being created is + temporary and a record about it should not be added into the ipa-clones dump + file. + If PARAM_ADJUSTMENTS is non-NULL, the parameter manipulation information will be overwritten by the new structure. Otherwise the new node will share parameter manipulation information with the original node. */ @@ -994,11 +1007,12 @@ cgraph_node::create_version_clone (tree new_decl, TREE_MAP is a mapping of tree nodes we want to replace with new ones (according to results of prior analysis). - If non-NULL ARGS_TO_SKIP determine function parameters to remove - from new version. - If SKIP_RETURN is true, the new version will return void. + If non-NULL PARAM_ADJUSTMENTS determine how function formal parameters + should be modified in the new version and if it should return void. If non-NULL BLOCK_TO_COPY determine what basic blocks to copy. If non_NULL NEW_ENTRY determine new entry BB of the clone. + SUFFIX is a string that will be used to create a new name for the new + function. If TARGET_ATTRIBUTES is non-null, when creating a new declaration, add the attributes to DECL_ATTRIBUTES. And call valid_attribute_p diff --git a/gcc/config/riscv/autovec-opt.md b/gcc/config/riscv/autovec-opt.md index 0c3b0cc..7cf7e8a 100644 --- a/gcc/config/riscv/autovec-opt.md +++ b/gcc/config/riscv/autovec-opt.md @@ -1673,3 +1673,26 @@ DONE; } [(set_attr "type" "vandn")]) + + +;; ============================================================================= +;; Combine vec_duplicate + op.vv to op.vx +;; Include +;; - vadd.vx +;; ============================================================================= +(define_insn_and_split "*<optab>_vx_<mode>" + [(set (match_operand:V_VLSI 0 "register_operand") + (any_int_binop_no_shift_vx:V_VLSI + (vec_duplicate:V_VLSI + (match_operand:<VEL> 1 "register_operand")) + (match_operand:V_VLSI 2 "<binop_rhs2_predicate>")))] + "TARGET_VECTOR && can_create_pseudo_p ()" + "#" + "&& 1" + [(const_int 0)] + { + rtx ops[] = {operands[0], operands[2], operands[1]}; + riscv_vector::emit_vlmax_insn (code_for_pred_scalar (<CODE>, <MODE>mode), + riscv_vector::BINARY_OP, ops); + } + [(set_attr "type" "vialu")]) diff --git a/gcc/config/riscv/bitmanip.md b/gcc/config/riscv/bitmanip.md index 20d03dc..95df533 100644 --- a/gcc/config/riscv/bitmanip.md +++ b/gcc/config/riscv/bitmanip.md @@ -1302,3 +1302,77 @@ } DONE; }) + +;; More forms of single bit extraction. The RISC-V port does not +;; define SHIFT_COUNT_TRUNCATED so we need forms where the bit position +;; is masked. +;; +;; We could in theory use this for rv32 as well, but it probably does +;; not occur in practice. The bit position would need to be QI/HI mode, +;; otherwise we would not need the zero extension. +;; +;; One could also argue that the zero extension is redundant and should +;; have been optimized away during RTL simplification. +(define_insn "*bextdi_position_ze_masked" + [(set (match_operand:DI 0 "register_operand" "=r") + (zero_extract:DI (match_operand:DI 1 "register_operand" "r") + (const_int 1) + (zero_extend:DI + (and:SI (match_operand:SI 2 "register_operand" "r") + (const_int 63)))))] + "TARGET_64BIT && TARGET_ZBS" + "bext\t%0,%1,%2" + [(set_attr "type" "bitmanip")]) + +;; Same as above, but without the extraneous zero_extend. +(define_insn "*bextdi_position_ze_masked" + [(set (match_operand:X 0 "register_operand" "=r") + (zero_extract:X + (match_operand:X 1 "register_operand" "r") + (const_int 1) + (and:X (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "bitpos_mask_operand" "n"))))] + "TARGET_64BIT && TARGET_ZBS" + "bext\t%0,%1,%2" + [(set_attr "type" "bitmanip")]) + + +;; Single bit extraction by first shifting it into the sign bit, then +;; shifting it down to the low bit. +(define_insn "*bext<mode>_position_masked" + [(set (match_operand:X 0 "register_operand" "=r") + (lshiftrt:X (ashift:X (match_operand:X 1 "register_operand" "r") + (match_operand:QI 2 "register_operand" "r")) + (match_operand:X 3 "bitpos_mask_operand" "n")))] + "TARGET_ZBS" + "bext\t%0,%1,%2" + [(set_attr "type" "bitmanip")]) + +;; Single bit extraction by shifting into the low bit, but with the +;; position formed with a subreg of a mask. +(define_insn "*bext<mode>_position_masked_subreg" + [(set (match_operand:X 0 "register_operand" "=r") + (lshiftrt:X + (ashift:X (match_operand:X 1 "register_operand" "r") + (subreg:QI + (and:X (match_operand:X 2 "register_operand" "r") + (match_operand:X 3 "bitpos_mask_operand" "n")) 0)) + (match_operand:X 4 "bitpos_mask_operand" "n")))] + "TARGET_ZBS" + "bext\t%0,%1,%2" + [(set_attr "type" "bitmanip")]) + +;; This has shown up in testing. In particular we end up with an +;; immediate input. We can load that into a register and target +;; one of the above bext patterns. +(define_split + [(set (match_operand:X 0 "register_operand") + (and:X (lshiftrt:X (match_operand 1 "immediate_operand") + (match_operand:QI 2 "register_operand")) + (const_int 1))) + (clobber (match_operand:X 3 "register_operand"))] + "" + [(set (match_dup 3) (match_dup 1)) + (set (match_dup 0) (zero_extract:X (match_dup 3) + (const_int 1) + (zero_extend:X (match_dup 2))))]) diff --git a/gcc/config/riscv/predicates.md b/gcc/config/riscv/predicates.md index f26bafc..c9a638c 100644 --- a/gcc/config/riscv/predicates.md +++ b/gcc/config/riscv/predicates.md @@ -685,3 +685,7 @@ (and (match_operand 0 "register_operand") (match_test "REGNO (op) == RETURN_ADDR_REGNUM || REGNO (op) == T0_REGNUM"))) + +(define_predicate "bitpos_mask_operand" + (and (match_code "const_int") + (match_test "TARGET_64BIT ? INTVAL (op) == 63 : INTVAL (op) == 31"))) diff --git a/gcc/config/riscv/riscv-opts.h b/gcc/config/riscv/riscv-opts.h index 26fe228..9766b89 100644 --- a/gcc/config/riscv/riscv-opts.h +++ b/gcc/config/riscv/riscv-opts.h @@ -162,4 +162,6 @@ enum riscv_tls_type { #define TARGET_VECTOR_AUTOVEC_SEGMENT \ (TARGET_VECTOR && riscv_mautovec_segment) +#define GPR2VR_COST_UNPROVIDED -1 + #endif /* ! GCC_RISCV_OPTS_H */ diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h index 2e88990..b0d5bbb 100644 --- a/gcc/config/riscv/riscv-protos.h +++ b/gcc/config/riscv/riscv-protos.h @@ -836,6 +836,7 @@ struct riscv_tune_info { const struct riscv_tune_info * riscv_parse_tune (const char *, bool); const cpu_vector_cost *get_vector_costs (); +int get_gr2vr_cost (); enum { diff --git a/gcc/config/riscv/riscv-vector-costs.cc b/gcc/config/riscv/riscv-vector-costs.cc index 167375c..c28eecd 100644 --- a/gcc/config/riscv/riscv-vector-costs.cc +++ b/gcc/config/riscv/riscv-vector-costs.cc @@ -1121,7 +1121,7 @@ costs::adjust_stmt_cost (enum vect_cost_for_stmt kind, loop_vec_info loop, { case scalar_to_vec: stmt_cost += (FLOAT_TYPE_P (vectype) ? costs->regmove->FR2VR - : costs->regmove->GR2VR); + : get_gr2vr_cost ()); break; case vec_to_scalar: stmt_cost += (FLOAT_TYPE_P (vectype) ? costs->regmove->VR2FR diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc index a065732..3ee88db 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.cc @@ -3863,7 +3863,40 @@ riscv_rtx_costs (rtx x, machine_mode mode, int outer_code, int opno ATTRIBUTE_UN Cost Model need to be well analyzed and supported in the future. */ if (riscv_v_ext_mode_p (mode)) { - *total = COSTS_N_INSNS (1); + int gr2vr_cost = get_gr2vr_cost (); + + switch (outer_code) + { + case SET: + { + switch (GET_CODE (x)) + { + case VEC_DUPLICATE: + *total = gr2vr_cost * COSTS_N_INSNS (1); + break; + case PLUS: + { + rtx op_0 = XEXP (x, 0); + rtx op_1 = XEXP (x, 1); + + if (GET_CODE (op_0) == VEC_DUPLICATE + || GET_CODE (op_1) == VEC_DUPLICATE) + *total = (gr2vr_cost + 1) * COSTS_N_INSNS (1); + else + *total = COSTS_N_INSNS (1); + } + break; + default: + *total = COSTS_N_INSNS (1); + break; + } + } + break; + default: + *total = COSTS_N_INSNS (1); + break; + } + return true; } @@ -9690,7 +9723,7 @@ riscv_register_move_cost (machine_mode mode, if (to == V_REGS) { if (from_is_gpr) - return get_vector_costs ()->regmove->GR2VR; + return get_gr2vr_cost (); else if (from_is_fpr) return get_vector_costs ()->regmove->FR2VR; } @@ -12540,6 +12573,21 @@ get_vector_costs () return costs; } +/* Return the cost of operation that move from gpr to vr. + It will take the value of --param=gpr2vr_cost if it is provided. + Or the default regmove->GR2VR will be returned. */ + +int +get_gr2vr_cost () +{ + int cost = get_vector_costs ()->regmove->GR2VR; + + if (gpr2vr_cost != GPR2VR_COST_UNPROVIDED) + cost = gpr2vr_cost; + + return cost; +} + /* Implement targetm.vectorize.builtin_vectorization_cost. */ static int @@ -12606,7 +12654,7 @@ riscv_builtin_vectorization_cost (enum vect_cost_for_stmt type_of_cost, { /* TODO: This is too pessimistic in case we can splat. */ int regmove_cost = fp ? costs->regmove->FR2VR - : costs->regmove->GR2VR; + : get_gr2vr_cost (); return (regmove_cost + common_costs->scalar_to_vec_cost) * estimated_poly_value (TYPE_VECTOR_SUBPARTS (vectype)); } diff --git a/gcc/config/riscv/riscv.opt b/gcc/config/riscv/riscv.opt index 7515c8e..7102480 100644 --- a/gcc/config/riscv/riscv.opt +++ b/gcc/config/riscv/riscv.opt @@ -579,6 +579,10 @@ Inline strlen calls if possible. Target RejectNegative Joined UInteger Var(riscv_strcmp_inline_limit) Init(64) Max number of bytes to compare as part of inlined strcmp/strncmp routines (default: 64). +-param=gpr2vr-cost= +Target RejectNegative Joined UInteger Var(gpr2vr_cost) Init(GPR2VR_COST_UNPROVIDED) +Set the cost value of the rvv instruction when operate from GPR to VR. + Enum Name(rvv_max_lmul) Type(enum rvv_max_lmul_enum) The RVV possible LMUL (-mrvv-max-lmul=): diff --git a/gcc/config/riscv/vector-iterators.md b/gcc/config/riscv/vector-iterators.md index b4c86909..eae3340 100644 --- a/gcc/config/riscv/vector-iterators.md +++ b/gcc/config/riscv/vector-iterators.md @@ -4041,6 +4041,10 @@ smax umax smin umin mult div udiv mod umod ]) +(define_code_iterator any_int_binop_no_shift_vx [ + plus +]) + (define_code_iterator any_int_unop [neg not]) (define_code_iterator any_commutative_binop [plus and ior xor diff --git a/gcc/diagnostic-client-data-hooks.h b/gcc/diagnostic-client-data-hooks.h index 0057b80..9909172 100644 --- a/gcc/diagnostic-client-data-hooks.h +++ b/gcc/diagnostic-client-data-hooks.h @@ -21,6 +21,8 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_DIAGNOSTIC_CLIENT_DATA_HOOKS_H #define GCC_DIAGNOSTIC_CLIENT_DATA_HOOKS_H +#include "logical-location.h" + class sarif_object; class client_version_info; @@ -35,8 +37,13 @@ class diagnostic_client_data_hooks /* Get version info for this client, or NULL. */ virtual const client_version_info *get_any_version_info () const = 0; - /* Get the current logical_location for this client, or NULL. */ - virtual const logical_location *get_current_logical_location () const = 0; + /* Get the current logical_location_manager for this client, or NULL. */ + virtual const logical_location_manager *get_logical_location_manager () const = 0; + + /* Get the current logical_location, or null. + If this returns a non-null logical_location, then + get_logical_location_manager must return non-NULL. */ + virtual logical_location get_current_logical_location () const = 0; /* Get a sourceLanguage value for FILENAME, or return NULL. See SARIF v2.1.0 Appendix J for suggested values. */ diff --git a/gcc/diagnostic-format-json.cc b/gcc/diagnostic-format-json.cc index c28804e..205678d 100644 --- a/gcc/diagnostic-format-json.cc +++ b/gcc/diagnostic-format-json.cc @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic-buffer.h" #include "json.h" #include "selftest.h" +#include "diagnostic-client-data-hooks.h" #include "logical-location.h" class json_output_format; @@ -272,11 +273,13 @@ make_json_for_path (diagnostic_context &context, auto pp = ref_pp->clone (); event.print_desc (*pp.get ()); event_obj->set_string ("description", pp_formatted_text (pp.get ())); - if (const logical_location *logical_loc = event.get_logical_location ()) - { - label_text name (logical_loc->get_name_for_path_output ()); - event_obj->set_string ("function", name.get ()); - } + if (logical_location logical_loc = event.get_logical_location ()) + if (auto hooks = context.get_client_data_hooks ()) + if (auto mgr = hooks->get_logical_location_manager ()) + { + label_text name (mgr->get_name_for_path_output (logical_loc)); + event_obj->set_string ("function", name.get ()); + } event_obj->set_integer ("depth", event.get_stack_depth ()); path_array->append (std::move (event_obj)); } diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc index bc6abdf..454eaae 100644 --- a/gcc/diagnostic-format-sarif.cc +++ b/gcc/diagnostic-format-sarif.cc @@ -51,6 +51,52 @@ along with GCC; see the file COPYING3. If not see #include "demangle.h" #include "backtrace.h" +/* A json::array where the values are "unique" as per + SARIF v2.1.0 section 3.7.3 ("Array properties with unique values"). */ + +template <typename JsonElementType> +class sarif_array_of_unique : public json::array +{ + public: + size_t append_uniquely (std::unique_ptr<JsonElementType> val) + { + /* This should be O(log(n)) due to the std::map. */ + auto search = m_index_by_value.find (val.get ()); + if (search != m_index_by_value.end()) + return (*search).second; + + const size_t insertion_idx = size (); + m_index_by_value.insert ({val.get (), insertion_idx}); + append (std::move (val)); + return insertion_idx; + } + + /* For ease of reading output, add "index": idx to all + objects in the array. + We don't do this until we've added everything, since + the "index" property would otherwise confuse the + comparison against new elements. */ + void add_explicit_index_values () + { + for (size_t idx = 0; idx < length (); ++idx) + if (json::object *obj = get (idx)->dyn_cast_object ()) + obj->set_integer ("index", idx); + } + +private: + struct comparator_t { + bool operator () (const json::value *a, const json::value *b) const + { + gcc_assert (a); + gcc_assert (b); + return json::value::compare (*a, *b) < 0; + } + }; + + // json::value * here is borrowed from m_elements + std::map<json::value *, int, comparator_t> m_index_by_value; +}; + /* Forward decls. */ class sarif_builder; class content_renderer; @@ -446,6 +492,13 @@ class sarif_physical_location : public sarif_object {}; class sarif_region : public sarif_object {}; +/* Subclass of sarif_object for SARIF "logicalLocation" objects + (SARIF v2.1.0 section 3.33). */ + +class sarif_logical_location : public sarif_object +{ +}; + /* Subclass of sarif_object for SARIF "locationRelationship" objects (SARIF v2.1.0 section 3.34). */ @@ -708,6 +761,12 @@ public: m_printer = &printer; } + const logical_location_manager * + get_logical_location_manager () const + { + return m_logical_loc_mgr; + } + void on_report_diagnostic (const diagnostic_info &diagnostic, diagnostic_t orig_diag_kind, diagnostic_sarif_format_buffer *buffer); @@ -729,7 +788,7 @@ public: std::unique_ptr<sarif_location> make_location_object (sarif_location_manager &loc_mgr, const rich_location &rich_loc, - const logical_location *logical_loc, + logical_location logical_loc, enum diagnostic_artifact_role role); std::unique_ptr<sarif_location> make_location_object (sarif_location_manager &loc_mgr, @@ -766,6 +825,9 @@ public: const sarif_generation_options &get_opts () const { return m_sarif_gen_opts; } + std::unique_ptr<sarif_logical_location> + make_minimal_sarif_logical_location (logical_location); + private: class sarif_token_printer : public token_printer { @@ -790,7 +852,7 @@ private: location_t where); void set_any_logical_locs_arr (sarif_location &location_obj, - const logical_location *logical_loc); + logical_location logical_loc); std::unique_ptr<sarif_location> make_location_object (sarif_location_manager &loc_mgr, const diagnostic_event &event, @@ -823,6 +885,10 @@ private: const content_renderer *snippet_renderer) const; std::unique_ptr<sarif_region> make_region_object_for_hint (const fixit_hint &hint) const; + + int + ensure_sarif_logical_location_for (logical_location k); + std::unique_ptr<sarif_multiformat_message_string> make_multiformat_message_string (const char *msg) const; std::unique_ptr<sarif_log> @@ -879,6 +945,8 @@ private: const line_maps *m_line_maps; sarif_token_printer m_token_printer; + const logical_location_manager *m_logical_loc_mgr; + /* The JSON object for the invocation object. */ std::unique_ptr<sarif_invocation> m_invocation_obj; @@ -901,6 +969,8 @@ private: /* The set of all CWE IDs we've seen, if any. */ hash_set <int_hash <int, 0, 1> > m_cwe_id_set; + std::unique_ptr<sarif_array_of_unique<sarif_logical_location>> m_cached_logical_locs; + int m_tabstop; std::unique_ptr<sarif_serialization_format> m_serialization_format; @@ -1232,7 +1302,8 @@ sarif_result::on_nested_diagnostic (const diagnostic_info &diagnostic, sometimes these will related to current_function_decl, but often they won't. */ auto location_obj - = builder.make_location_object (*this, *diagnostic.richloc, nullptr, + = builder.make_location_object (*this, *diagnostic.richloc, + logical_location (), diagnostic_artifact_role::result_file); auto message_obj = builder.make_message_object (pp_formatted_text (builder.get_printer ())); @@ -1579,6 +1650,7 @@ sarif_builder::sarif_builder (diagnostic_context &context, m_printer (&printer), m_line_maps (line_maps), m_token_printer (*this), + m_logical_loc_mgr (nullptr), m_invocation_obj (std::make_unique<sarif_invocation> (*this, context.get_original_argv ())), @@ -1587,6 +1659,8 @@ sarif_builder::sarif_builder (diagnostic_context &context, m_seen_any_relative_paths (false), m_rule_id_set (), m_rules_arr (new json::array ()), + m_cached_logical_locs + (std::make_unique<sarif_array_of_unique<sarif_logical_location>> ()), m_tabstop (context.m_tabstop), m_serialization_format (std::move (serialization_format)), m_sarif_gen_opts (sarif_gen_opts), @@ -1596,6 +1670,9 @@ sarif_builder::sarif_builder (diagnostic_context &context, gcc_assert (m_line_maps); gcc_assert (m_serialization_format); + if (auto client_data_hooks = context.get_client_data_hooks ()) + m_logical_loc_mgr = client_data_hooks->get_logical_location_manager (); + /* Mark MAIN_INPUT_FILENAME_ as the artifact that the tool was instructed to scan. Only quote the contents if it gets referenced by physical locations, @@ -2089,7 +2166,7 @@ sarif_builder::make_locations_arr (sarif_location_manager &loc_mgr, enum diagnostic_artifact_role role) { auto locations_arr = std::make_unique<json::array> (); - const logical_location *logical_loc = nullptr; + logical_location logical_loc; if (auto client_data_hooks = m_context.get_client_data_hooks ()) logical_loc = client_data_hooks->get_current_logical_location (); @@ -2103,18 +2180,24 @@ sarif_builder::make_locations_arr (sarif_location_manager &loc_mgr, } /* If LOGICAL_LOC is non-null, use it to create a "logicalLocations" property - within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4). */ + within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4) with a minimal logical + location object referencing theRuns.logicalLocations (3.33.3). */ void sarif_builder:: set_any_logical_locs_arr (sarif_location &location_obj, - const logical_location *logical_loc) + logical_location logical_loc) { if (!logical_loc) return; + gcc_assert (m_logical_loc_mgr); auto location_locs_arr = std::make_unique<json::array> (); + + auto logical_loc_obj = make_minimal_sarif_logical_location (logical_loc); + location_locs_arr->append<sarif_logical_location> - (make_sarif_logical_location_object (*logical_loc)); + (std::move (logical_loc_obj)); + location_obj.set<json::array> ("logicalLocations", std::move (location_locs_arr)); } @@ -2127,7 +2210,7 @@ set_any_logical_locs_arr (sarif_location &location_obj, std::unique_ptr<sarif_location> sarif_builder::make_location_object (sarif_location_manager &loc_mgr, const rich_location &rich_loc, - const logical_location *logical_loc, + logical_location logical_loc, enum diagnostic_artifact_role role) { class escape_nonascii_renderer : public content_renderer @@ -2321,7 +2404,7 @@ sarif_builder::make_location_object (sarif_location_manager &loc_mgr, std::move (phs_loc_obj)); /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */ - const logical_location *logical_loc = event.get_logical_location (); + logical_location logical_loc = event.get_logical_location (); set_any_logical_locs_arr (*location_obj, logical_loc); /* "message" property (SARIF v2.1.0 section 3.28.5). */ @@ -2651,6 +2734,7 @@ maybe_get_sarif_kind (enum logical_location_kind kind) case LOGICAL_LOCATION_KIND_UNKNOWN: return nullptr; + /* Kinds within executable code. */ case LOGICAL_LOCATION_KIND_FUNCTION: return "function"; case LOGICAL_LOCATION_KIND_MEMBER: @@ -2667,35 +2751,120 @@ maybe_get_sarif_kind (enum logical_location_kind kind) return "parameter"; case LOGICAL_LOCATION_KIND_VARIABLE: return "variable"; + + /* Kinds within XML or HTML documents. */ + case LOGICAL_LOCATION_KIND_ELEMENT: + return "element"; + case LOGICAL_LOCATION_KIND_ATTRIBUTE: + return "attribute"; + case LOGICAL_LOCATION_KIND_TEXT: + return "text"; + case LOGICAL_LOCATION_KIND_COMMENT: + return "comment"; + case LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION: + return "processingInstruction"; + case LOGICAL_LOCATION_KIND_DTD: + return "dtd"; + case LOGICAL_LOCATION_KIND_DECLARATION: + return "declaration"; + + /* Kinds within JSON documents. */ + case LOGICAL_LOCATION_KIND_OBJECT: + return "object"; + case LOGICAL_LOCATION_KIND_ARRAY: + return "array"; + case LOGICAL_LOCATION_KIND_PROPERTY: + return "property"; + case LOGICAL_LOCATION_KIND_VALUE: + return "value"; } } -/* Make a "logicalLocation" object (SARIF v2.1.0 section 3.33) for LOGICAL_LOC, - or return nullptr. */ +/* Set PROPERTY_NAME within this bag to a "logicalLocation" object (SARIF v2.1.0 + section 3.33) for LOGICAL_LOC. The object has an "index" property to refer to + theRuns.logicalLocations (3.33.3). */ -std::unique_ptr<sarif_logical_location> -make_sarif_logical_location_object (const logical_location &logical_loc) +void +sarif_property_bag::set_logical_location (const char *property_name, + sarif_builder &builder, + logical_location logical_loc) { - auto logical_loc_obj = std::make_unique<sarif_logical_location> (); + set<sarif_logical_location> + (property_name, + builder.make_minimal_sarif_logical_location (logical_loc)); +} - /* "name" property (SARIF v2.1.0 section 3.33.4). */ - if (const char *short_name = logical_loc.get_short_name ()) - logical_loc_obj->set_string ("name", short_name); +/* Ensure that m_cached_logical_locs has a "logicalLocation" object + (SARIF v2.1.0 section 3.33) for K, and return its index within the + array. */ + +int +sarif_builder:: +ensure_sarif_logical_location_for (logical_location k) +{ + gcc_assert (m_logical_loc_mgr); + + auto sarif_logical_loc = std::make_unique<sarif_logical_location> (); + + if (const char *short_name = m_logical_loc_mgr->get_short_name (k)) + sarif_logical_loc->set_string ("name", short_name); /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */ - if (const char *name_with_scope = logical_loc.get_name_with_scope ()) - logical_loc_obj->set_string ("fullyQualifiedName", name_with_scope); + if (const char *name_with_scope = m_logical_loc_mgr->get_name_with_scope (k)) + sarif_logical_loc->set_string ("fullyQualifiedName", name_with_scope); /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */ - if (const char *internal_name = logical_loc.get_internal_name ()) - logical_loc_obj->set_string ("decoratedName", internal_name); + if (const char *internal_name = m_logical_loc_mgr->get_internal_name (k)) + sarif_logical_loc->set_string ("decoratedName", internal_name); /* "kind" property (SARIF v2.1.0 section 3.33.7). */ - enum logical_location_kind kind = logical_loc.get_kind (); + enum logical_location_kind kind = m_logical_loc_mgr->get_kind (k); if (const char *sarif_kind_str = maybe_get_sarif_kind (kind)) - logical_loc_obj->set_string ("kind", sarif_kind_str); + sarif_logical_loc->set_string ("kind", sarif_kind_str); + + /* "parentIndex" property (SARIF v2.1.0 section 3.33.8). */ + if (auto parent_key = m_logical_loc_mgr->get_parent (k)) + { + /* Recurse upwards. */ + int parent_index = ensure_sarif_logical_location_for (parent_key); + sarif_logical_loc->set_integer ("parentIndex", parent_index); + } + + /* Consolidate if this logical location already exists. */ + int index + = m_cached_logical_locs->append_uniquely (std::move (sarif_logical_loc)); - return logical_loc_obj; + return index; +} + +/* Ensure that theRuns.logicalLocations (3.14.17) has a "logicalLocation" object + (SARIF v2.1.0 section 3.33) for LOGICAL_LOC. + Create and return a minimal logicalLocation object referring to the + full object by index. */ + +std::unique_ptr<sarif_logical_location> +sarif_builder:: +make_minimal_sarif_logical_location (logical_location logical_loc) +{ + gcc_assert (m_logical_loc_mgr); + + /* Ensure that m_cached_logical_locs has a "logicalLocation" object + (SARIF v2.1.0 section 3.33) for LOGICAL_LOC, and return its index within + the array. */ + + auto sarif_logical_loc = std::make_unique <sarif_logical_location> (); + + int index = ensure_sarif_logical_location_for (logical_loc); + + // 3.33.3 index property + sarif_logical_loc->set_integer ("index", index); + + /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */ + if (const char *name_with_scope + = m_logical_loc_mgr->get_name_with_scope (logical_loc)) + sarif_logical_loc->set_string ("fullyQualifiedName", name_with_scope); + + return sarif_logical_loc; } label_text @@ -2784,7 +2953,7 @@ populate_thread_flow_location_object (sarif_result &result, { /* Give diagnostic_event subclasses a chance to add custom properties via a property bag. */ - ev.maybe_add_sarif_properties (tfl_obj); + ev.maybe_add_sarif_properties (*this, tfl_obj); /* "location" property (SARIF v2.1.0 section 3.38.3). */ tfl_obj.set<sarif_location> @@ -3038,6 +3207,14 @@ make_run_object (std::unique_ptr<sarif_invocation> invocation_obj, /* "results" property (SARIF v2.1.0 section 3.14.23). */ run_obj->set<json::array> ("results", std::move (results)); + /* "logicalLocations" property (SARIF v2.1.0 3.14.17). */ + if (m_cached_logical_locs->size () > 0) + { + m_cached_logical_locs->add_explicit_index_values (); + run_obj->set<json::array> ("logicalLocations", + std::move (m_cached_logical_locs)); + } + return run_obj; } @@ -3924,6 +4101,76 @@ sarif_generation_options::sarif_generation_options () namespace selftest { +static void +test_sarif_array_of_unique_1 () +{ + sarif_array_of_unique<json::string> arr; + + ASSERT_EQ (arr.length (), 0); + + { + size_t idx = arr.append_uniquely (std::make_unique<json::string> ("foo")); + ASSERT_EQ (idx, 0); + ASSERT_EQ (arr.length (), 1); + } + { + size_t idx = arr.append_uniquely (std::make_unique<json::string> ("bar")); + ASSERT_EQ (idx, 1); + ASSERT_EQ (arr.length (), 2); + } + + /* Try adding them again, should be idempotent. */ + { + size_t idx = arr.append_uniquely (std::make_unique<json::string> ("foo")); + ASSERT_EQ (idx, 0); + ASSERT_EQ (arr.length (), 2); + } + { + size_t idx = arr.append_uniquely (std::make_unique<json::string> ("bar")); + ASSERT_EQ (idx, 1); + ASSERT_EQ (arr.length (), 2); + } +} + +static void +test_sarif_array_of_unique_2 () +{ + sarif_array_of_unique<json::object> arr; + + ASSERT_EQ (arr.length (), 0); + + { + auto obj0 = std::make_unique<json::object> (); + size_t idx = arr.append_uniquely (std::move (obj0)); + ASSERT_EQ (idx, 0); + ASSERT_EQ (arr.length (), 1); + + // Attempting to add another empty objects should be idempotent. + idx = arr.append_uniquely (std::make_unique<json::object> ()); + ASSERT_EQ (idx, 0); + ASSERT_EQ (arr.length (), 1); + } + { + auto obj1 = std::make_unique<json::object> (); + obj1->set_string ("foo", "bar"); + size_t idx = arr.append_uniquely (std::move (obj1)); + ASSERT_EQ (idx, 1); + ASSERT_EQ (arr.length (), 2); + + // Attempting to add an equivalent object should be idempotent. + auto other = std::make_unique<json::object> (); + other->set_string ("foo", "bar"); + idx = arr.append_uniquely (std::move (other)); + ASSERT_EQ (idx, 1); + ASSERT_EQ (arr.length (), 2); + } + + // Verify behavior of add_explicit_index_values. + arr.add_explicit_index_values (); + ASSERT_JSON_INT_PROPERTY_EQ (arr[0], "index", 0); + ASSERT_JSON_INT_PROPERTY_EQ (arr[1], "index", 1); +} + /* A subclass of sarif_output_format for writing selftests. The JSON output is cached internally, rather than written out to a file. */ @@ -4029,7 +4276,8 @@ test_make_location_object (const sarif_generation_options &sarif_gen_opts, std::unique_ptr<sarif_location> location_obj = builder.make_location_object - (result, richloc, nullptr, diagnostic_artifact_role::analysis_target); + (result, richloc, logical_location (), + diagnostic_artifact_role::analysis_target); ASSERT_NE (location_obj, nullptr); auto physical_location @@ -4588,6 +4836,9 @@ run_line_table_case_tests_per_version (const line_table_case &case_) void diagnostic_format_sarif_cc_tests () { + test_sarif_array_of_unique_1 (); + test_sarif_array_of_unique_2 (); + for_each_sarif_gen_option (test_simple_log); for_each_sarif_gen_option (test_message_with_embedded_link); for_each_sarif_gen_option (test_message_with_braces); diff --git a/gcc/diagnostic-format-sarif.h b/gcc/diagnostic-format-sarif.h index 6446257..4e6a525 100644 --- a/gcc/diagnostic-format-sarif.h +++ b/gcc/diagnostic-format-sarif.h @@ -24,8 +24,7 @@ along with GCC; see the file COPYING3. If not see #include "json.h" #include "diagnostic-format.h" #include "diagnostic-output-file.h" - -class logical_location; +#include "logical-location.h" /* Enum for choosing what format to serializing the generated SARIF into. */ @@ -115,11 +114,17 @@ make_sarif_sink (diagnostic_context &context, const sarif_generation_options &sarif_gen_opts, diagnostic_output_file output_file); +class sarif_builder; + /* Concrete subclass of json::object for SARIF property bags (SARIF v2.1.0 section 3.8). */ class sarif_property_bag : public json::object { +public: + void set_logical_location (const char *property_name, + sarif_builder &, + logical_location logical_loc); }; /* Concrete subclass of json::object for SARIF objects that can @@ -134,14 +139,4 @@ public: sarif_property_bag &get_or_create_properties (); }; -/* Subclass of sarif_object for SARIF "logicalLocation" objects - (SARIF v2.1.0 section 3.33). */ - -class sarif_logical_location : public sarif_object -{ -}; - -extern std::unique_ptr<sarif_logical_location> -make_sarif_logical_location_object (const logical_location &logical_loc); - #endif /* ! GCC_DIAGNOSTIC_FORMAT_SARIF_H */ diff --git a/gcc/diagnostic-path.cc b/gcc/diagnostic-path.cc index d618c37..9340e4e 100644 --- a/gcc/diagnostic-path.cc +++ b/gcc/diagnostic-path.cc @@ -181,8 +181,8 @@ diagnostic_path::get_first_event_in_a_function (unsigned *out_idx) const for (unsigned i = 0; i < num; i++) { const diagnostic_event &event = get_event (i); - if (const logical_location *logical_loc = event.get_logical_location ()) - if (logical_loc->function_p ()) + if (logical_location logical_loc = event.get_logical_location ()) + if (m_logical_loc_mgr.function_p (logical_loc)) { *out_idx = i; return true; @@ -409,8 +409,10 @@ class per_thread_summary { public: per_thread_summary (const diagnostic_path &path, + const logical_location_manager &logical_loc_mgr, label_text name, unsigned swimlane_idx) : m_path (path), + m_logical_loc_mgr (logical_loc_mgr), m_name (std::move (name)), m_swimlane_idx (swimlane_idx), m_last_event (nullptr), @@ -437,6 +439,7 @@ private: friend struct event_range; const diagnostic_path &m_path; + const logical_location_manager &m_logical_loc_mgr; const label_text m_name; @@ -703,7 +706,7 @@ struct event_range const diagnostic_path &m_path; const diagnostic_event &m_initial_event; - const logical_location *m_logical_loc; + logical_location m_logical_loc; int m_stack_depth; unsigned m_start_idx; unsigned m_end_idx; @@ -729,6 +732,10 @@ struct path_summary bool colorize = false, bool show_event_links = true); + const logical_location_manager &get_logical_location_manager () const + { + return m_logical_loc_mgr; + } unsigned get_num_ranges () const { return m_ranges.length (); } bool multithreaded_p () const { return m_per_thread_summary.length () > 1; } @@ -740,6 +747,7 @@ struct path_summary return **slot; } + const logical_location_manager &m_logical_loc_mgr; auto_delete_vec <event_range> m_ranges; auto_delete_vec <per_thread_summary> m_per_thread_summary; hash_map<int_hash<diagnostic_thread_id_t, -1, -2>, @@ -756,6 +764,7 @@ private: const diagnostic_thread &thread = path.get_thread (tid); per_thread_summary *pts = new per_thread_summary (path, + m_logical_loc_mgr, thread.get_name (false), m_per_thread_summary.length ()); m_thread_id_to_events.put (tid, pts); @@ -792,6 +801,7 @@ path_summary::path_summary (const path_print_policy &policy, bool check_rich_locations, bool colorize, bool show_event_links) +: m_logical_loc_mgr (path.get_logical_location_manager ()) { const unsigned num_events = path.num_events (); @@ -872,6 +882,7 @@ public: void print_swimlane_for_event_range (diagnostic_text_output_format &text_output, pretty_printer *pp, + const logical_location_manager &logical_loc_mgr, event_range *range, diagnostic_source_effect_info *effect_info) { @@ -916,9 +927,10 @@ public: m_cur_indent += 5; } } - if (const logical_location *logical_loc = range->m_logical_loc) + if (range->m_logical_loc) { - label_text name (logical_loc->get_name_for_path_output ()); + label_text name + (logical_loc_mgr.get_name_for_path_output (range->m_logical_loc)); if (name.get ()) pp_printf (pp, "%qs: ", name.get ()); } @@ -1113,7 +1125,9 @@ print_path_summary_as_text (const path_summary &ps, of this range. */ diagnostic_source_effect_info effect_info; effect_info.m_leading_in_edge_column = last_out_edge_column; - tep.print_swimlane_for_event_range (text_output, pp, range, &effect_info); + tep.print_swimlane_for_event_range (text_output, pp, + ps.get_logical_location_manager (), + range, &effect_info); last_out_edge_column = effect_info.m_trailing_out_edge_column; } } @@ -1155,6 +1169,7 @@ diagnostic_text_output_format::print_path (const diagnostic_path &path) case DPF_SEPARATE_EVENTS: { /* A note per event. */ + auto &logical_loc_mgr = path.get_logical_location_manager (); for (unsigned i = 0; i < num_events; i++) { const diagnostic_event &event = path.get_event (i); @@ -1166,10 +1181,11 @@ diagnostic_text_output_format::print_path (const diagnostic_path &path) /* -fdiagnostics-path-format=separate-events doesn't print fndecl information, so with -fdiagnostics-show-path-depths print the fndecls too, if any. */ - if (const logical_location *logical_loc + if (logical_location logical_loc = event.get_logical_location ()) { - label_text name (logical_loc->get_name_for_path_output ()); + label_text name + (logical_loc_mgr.get_name_for_path_output (logical_loc)); inform (event.get_location (), "%@ %e (fndecl %qs, depth %i)", &event_id, &e_event_desc, diff --git a/gcc/diagnostic-path.h b/gcc/diagnostic-path.h index 878d6ff..4419139 100644 --- a/gcc/diagnostic-path.h +++ b/gcc/diagnostic-path.h @@ -23,7 +23,9 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic.h" /* for ATTRIBUTE_GCC_DIAG. */ #include "diagnostic-event-id.h" +#include "logical-location.h" +class sarif_builder; class sarif_object; /* A diagnostic_path is an optional additional piece of metadata associated @@ -149,8 +151,8 @@ class diagnostic_event /* Print a localized (and possibly colorized) description of this event. */ virtual void print_desc (pretty_printer &pp) const = 0; - /* Get a logical_location for this event, or nullptr if there is none. */ - virtual const logical_location *get_logical_location () const = 0; + /* Get a logical location for this event, or null if there is none. */ + virtual logical_location get_logical_location () const = 0; virtual meaning get_meaning () const = 0; @@ -163,7 +165,8 @@ class diagnostic_event /* Hook for SARIF output to allow for adding diagnostic-specific properties to the threadFlowLocation object's property bag. */ virtual void - maybe_add_sarif_properties (sarif_object &/*thread_flow_loc_obj*/) const + maybe_add_sarif_properties (sarif_builder &, + sarif_object &/*thread_flow_loc_obj*/) const { } @@ -203,8 +206,21 @@ class diagnostic_path bool interprocedural_p () const; bool multithreaded_p () const; + const logical_location_manager &get_logical_location_manager () const + { + return m_logical_loc_mgr; + } + +protected: + diagnostic_path (const logical_location_manager &logical_loc_mgr) + : m_logical_loc_mgr (logical_loc_mgr) + { + } + private: bool get_first_event_in_a_function (unsigned *out_idx) const; + + const logical_location_manager &m_logical_loc_mgr; }; /* Concrete subclasses of the above can be found in diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc index c3ea1b3..b43fc90 100644 --- a/gcc/diagnostic.cc +++ b/gcc/diagnostic.cc @@ -580,6 +580,14 @@ diagnostic_context::pop_urlifier () delete node.m_urlifier; } +const logical_location_manager * +diagnostic_context::get_logical_location_manager () const +{ + if (!m_client_data_hooks) + return nullptr; + return m_client_data_hooks->get_logical_location_manager (); +} + const urlifier * diagnostic_context::get_urlifier () const { @@ -1037,14 +1045,14 @@ diagnostic_context::notes_inhibited_in_group () const return false; } -/* class logical_location. */ +/* class logical_location_manager. */ /* Return true iff this is a function or method. */ bool -logical_location::function_p () const +logical_location_manager::function_p (key k) const { - switch (get_kind ()) + switch (get_kind (k)) { default: gcc_unreachable (); diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h index 88288d0..00d7812 100644 --- a/gcc/diagnostic.h +++ b/gcc/diagnostic.h @@ -217,7 +217,7 @@ public: class edit_context; class diagnostic_client_data_hooks; -class logical_location; +class logical_location_manager; class diagnostic_diagram; class diagnostic_source_effect_info; class diagnostic_output_format; @@ -671,6 +671,9 @@ public: return m_client_data_hooks; } + const logical_location_manager * + get_logical_location_manager () const; + const urlifier *get_urlifier () const; text_art::theme *get_diagram_theme () const { return m_diagrams.m_theme; } diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 32bc457..90cbb51 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -20774,6 +20774,93 @@ By default, the dump will contain messages about successful optimizations (equivalent to @option{-optimized}) together with low-level details about the analysis. +@opindex fdump-ipa-clones +@item -fdump-ipa-clones + +Create a dump file containing information about creation of call graph +node clones and removals of call graph nodes during inter-procedural +optimizations and transformations. Its main intended use is that tools +that create live-patches can determine the set of functions that need to +be live-patched to completely replace a particular function (see +@option{-flive-patching}). The file name is generated by appending +suffix @code{ipa-clones} to the source file name, and the file is +created in the same directory as the output file. Each entry in the +file is on a separate line containing semicolon separated fields. + +In the case of call graph clone creation, the individual fields are: + +@enumerate +@item +String @code{Callgraph clone}. + +@item +Name of the function being cloned as it is presented to the assembler. + +@item +A number that uniquely represents the function being cloned in the call +graph. Note that the number is unique only within a compilation unit or +within whole-program analysis but is likely to be different in the two +phases. + +@item +The file name of the source file where the function is defined. + +@item +The line on which the function definition is located. + +@item +The column where the function definition is located. + +@item +Name of the new function clone as it is presented to the assembler. + +@item +A number that uniquely represents the new function clone in the call +graph. Note that the number is unique only within a compilation unit or +within whole-program analysis but is likely to be different in the two +phases. + +@item +The file name of the source file where the source code location of the +new clone points to. + +@item +The line to which the source code location of the new clone points to. + +@item +The column to which the source code location of the new clone points to. + +@item +A string that determines the reason for cloning. + +@end enumerate + +In the case of call graph clone removal, the individual fields are: + +@enumerate +@item +String @code{Callgraph removal}. + +@item +Name of the function being removed as it would be presented to the assembler. + +@item +A number that uniquely represents the function being cloned in the call +graph. Note that the number is unique only within a compilation unit or +within whole-program analysis but is likely to be different in the two +phases. + +@item +The file name of the source file where the function is defined. + +@item +The line on which the function definition is located. + +@item +The column where the function definition is located. + +@end enumerate + @opindex fdump-lang @item -fdump-lang Dump language-specific information. The file name is made by appending diff --git a/gcc/doc/libgdiagnostics/topics/compatibility.rst b/gcc/doc/libgdiagnostics/topics/compatibility.rst new file mode 100644 index 0000000..4df6850 --- /dev/null +++ b/gcc/doc/libgdiagnostics/topics/compatibility.rst @@ -0,0 +1,179 @@ +.. Copyright (C) 2015-2025 Free Software Foundation, Inc. + Originally contributed by David Malcolm <dmalcolm@redhat.com> + + This is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + <https://www.gnu.org/licenses/>. + +.. default-domain:: c + +ABI and API compatibility +========================= + +The libgdiagnostics developers strive for ABI and API backward-compatibility: +programs built against libgdiagnostics.so stand a good chance of running +without recompilation against newer versions of libgdiagnostics.so, and +ought to recompile without modification against newer versions of +libgdiagnostics.h. + +.. note:: The libgdiagnostics++.h C++ API is more experimental, and less + locked-down at this time. + +API compatibility is achieved by extending the API rather than changing +it. For ABI compatiblity, we avoid bumping the SONAME, and instead use +symbol versioning to tag each symbol, so that a binary linked against +libgdiagnostics.so is tagged according to the symbols that it uses. + +For example, :func:`diagnostic_logical_location_get_kind` was added in +``LIBGDIAGNOSTICS_ABI_1``. If a client program uses it, this can be detected +from metadata by using ``objdump``: + +.. code-block:: bash + + $ objdump -p testsuite/libgdiagnostics/test-logical-location.c.exe | tail -n 7 + + Version References: + required from libc.so.6: + 0x09691a75 0x00 04 GLIBC_2.2.5 + required from libgdiagnostics.so.0: + 0x0ec567d1 0x00 03 LIBGDIAGNOSTICS_ABI_1 + 0x0ec567d0 0x00 02 LIBGDIAGNOSTICS_ABI_0 + +You can see the symbol tags provided by libgdiagnostics.so using ``objdump``: + +.. code-block:: bash + + $ objdump -p libgdiagnostics.so | less + [...snip...] + Version definitions: + 1 0x01 0x099ea4b0 libgdiagnostics.so.0 + 2 0x00 0x0ec567d0 LIBGDIAGNOSTICS_ABI_0 + 3 0x00 0x0ec567d1 LIBGDIAGNOSTICS_ABI_1 + LIBGDIAGNOSTICS_ABI_0 + [...snip...] + +ABI symbol tags +*************** + +.. _LIBGDIAGNOSTICS_ABI_0: + +``LIBGDIAGNOSTICS_ABI_0`` +------------------------- + +All entrypoints in the initial release of libgdiagnostics (in GCC 15) are +tagged with ``LIBGDIAGNOSTICS_ABI_0``; these entrypoints are: + + * :func:`diagnostic_manager_new` + + * :func:`diagnostic_manager_release` + + * :func:`diagnostic_manager_set_tool_name` + + * :func:`diagnostic_manager_set_full_name` + + * :func:`diagnostic_manager_set_version_string` + + * :func:`diagnostic_manager_set_version_url` + + * :func:`diagnostic_manager_add_text_sink` + + * :func:`diagnostic_text_sink_set_source_printing_enabled` + + * :func:`diagnostic_text_sink_set_colorize` + + * :func:`diagnostic_text_sink_set_labelled_source_colorization_enabled` + + * :func:`diagnostic_manager_add_sarif_sink` + + * :func:`diagnostic_manager_write_patch` + + * :func:`diagnostic_manager_new_file` + + * :func:`diagnostic_file_set_buffered_content` + + * :func:`diagnostic_manager_debug_dump_file` + + * :func:`diagnostic_manager_new_location_from_file_and_line` + + * :func:`diagnostic_manager_new_location_from_file_line_column` + + * :func:`diagnostic_manager_new_location_from_range` + + * :func:`diagnostic_manager_debug_dump_location` + + * :func:`diagnostic_manager_new_logical_location` + + * :func:`diagnostic_manager_debug_dump_logical_location` + + * :func:`diagnostic_manager_begin_group` + + * :func:`diagnostic_manager_end_group` + + * :func:`diagnostic_begin` + + * :func:`diagnostic_set_cwe` + + * :func:`diagnostic_add_rule` + + * :func:`diagnostic_set_location` + + * :func:`diagnostic_set_location_with_label` + + * :func:`diagnostic_add_location` + + * :func:`diagnostic_add_location_with_label` + + * :func:`diagnostic_set_logical_location` + + * :func:`diagnostic_add_fix_it_hint_insert_before` + + * :func:`diagnostic_add_fix_it_hint_insert_after` + + * :func:`diagnostic_add_fix_it_hint_replace` + + * :func:`diagnostic_add_fix_it_hint_delete` + + * :func:`diagnostic_add_execution_path` + + * :func:`diagnostic_manager_new_execution_path` + + * :func:`diagnostic_take_execution_path` + + * :func:`diagnostic_execution_path_release` + + * :func:`diagnostic_execution_path_add_event` + + * :func:`diagnostic_execution_path_add_event_va` + + * :func:`diagnostic_finish` + + * :func:`diagnostic_finish_va` + + * :func:`diagnostic_physical_location_get_file` + +.. _LIBGDIAGNOSTICS_ABI_1: + +``LIBGDIAGNOSTICS_ABI_1`` +------------------------- +``LIBGDIAGNOSTICS_ABI_1`` covers the addition of these functions for +acccessing values within a :type:`diagnostic_logical_location`: + + * :func:`diagnostic_logical_location_get_kind` + + * :func:`diagnostic_logical_location_get_parent` + + * :func:`diagnostic_logical_location_get_short_name` + + * :func:`diagnostic_logical_location_get_fully_qualified_name` + + * :func:`diagnostic_logical_location_get_decorated_name` diff --git a/gcc/doc/libgdiagnostics/topics/index.rst b/gcc/doc/libgdiagnostics/topics/index.rst index 6e14c0f..6eb3ed6 100644 --- a/gcc/doc/libgdiagnostics/topics/index.rst +++ b/gcc/doc/libgdiagnostics/topics/index.rst @@ -36,3 +36,4 @@ Topic reference text-output.rst sarif.rst ux.rst + compatibility.rst diff --git a/gcc/doc/libgdiagnostics/topics/logical-locations.rst b/gcc/doc/libgdiagnostics/topics/logical-locations.rst index 3fbee83..184b563 100644 --- a/gcc/doc/libgdiagnostics/topics/logical-locations.rst +++ b/gcc/doc/libgdiagnostics/topics/logical-locations.rst @@ -51,6 +51,8 @@ source location This roughly corresponds to the ``kind`` property in SARIF v2.1.0 (`§3.33.7 <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790976>`_). + Kinds within executable code: + .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER @@ -67,6 +69,32 @@ source location .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE + Kinds within XML or HTML documents: + + .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_ELEMENT + + .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_ATTRIBUTE + + .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_TEXT + + .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_COMMENT + + .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION + + .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_DTD + + .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_DECLARATION + + Kinds within JSON documents: + + .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT + + .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY + + .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY + + .. macro:: DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE + ``parent`` can be NULL; if non-NULL it can be used to express tree-like nesting of logical locations, such as in:: @@ -113,3 +141,28 @@ Associating diagnostics with locations Set the logical location of ``diag``. ``diag`` must be non-NULL; ``logical_loc`` can be NULL. + +Accessors +********* + +The following functions can be used to access the data that was passed to a +:type:`diagnostic_logical_location` when it was created. In each case, the +``loc`` parameter must be non-NULL. :type:`const char *` values will point +at copies of the original buffer. + +.. function:: enum diagnostic_logical_location_kind_t diagnostic_logical_location_get_kind (const diagnostic_logical_location *loc) + +.. function:: const diagnostic_logical_location *diagnostic_logical_location_get_parent (const diagnostic_logical_location *loc) + +.. function:: const char *diagnostic_logical_location_get_short_name (const diagnostic_logical_location *loc) + +.. function:: const char *diagnostic_logical_location_get_fully_qualified_name (const diagnostic_logical_location *loc) + +.. function:: const char *diagnostic_logical_location_get_decorated_name (const diagnostic_logical_location *loc) + +The above accessors were added in :ref:`LIBGDIAGNOSTICS_ABI_1`; you can +test for their presence using + + .. code-block:: c + + #ifdef LIBDIAGNOSTICS_HAVE_LOGICAL_LOCATION_ACCESSORS diff --git a/gcc/fortran/check.cc b/gcc/fortran/check.cc index 299c216..f02a2a3 100644 --- a/gcc/fortran/check.cc +++ b/gcc/fortran/check.cc @@ -5955,30 +5955,40 @@ gfc_check_c_sizeof (gfc_expr *arg) bool gfc_check_c_associated (gfc_expr *c_ptr_1, gfc_expr *c_ptr_2) { - if (c_ptr_1->ts.type != BT_DERIVED - || c_ptr_1->ts.u.derived->from_intmod != INTMOD_ISO_C_BINDING - || (c_ptr_1->ts.u.derived->intmod_sym_id != ISOCBINDING_PTR - && c_ptr_1->ts.u.derived->intmod_sym_id != ISOCBINDING_FUNPTR)) + if (c_ptr_1) { - gfc_error ("Argument C_PTR_1 at %L to C_ASSOCIATED shall have the " - "type TYPE(C_PTR) or TYPE(C_FUNPTR)", &c_ptr_1->where); - return false; + if (c_ptr_1->expr_type == EXPR_FUNCTION && c_ptr_1->ts.type == BT_VOID) + return true; + + if (c_ptr_1->ts.type != BT_DERIVED + || c_ptr_1->ts.u.derived->from_intmod != INTMOD_ISO_C_BINDING + || (c_ptr_1->ts.u.derived->intmod_sym_id != ISOCBINDING_PTR + && c_ptr_1->ts.u.derived->intmod_sym_id != ISOCBINDING_FUNPTR)) + { + gfc_error ("Argument C_PTR_1 at %L to C_ASSOCIATED shall have the " + "type TYPE(C_PTR) or TYPE(C_FUNPTR)", &c_ptr_1->where); + return false; + } } if (!scalar_check (c_ptr_1, 0)) return false; - if (c_ptr_2 - && (c_ptr_2->ts.type != BT_DERIVED + if (c_ptr_2) + { + if (c_ptr_2->expr_type == EXPR_FUNCTION && c_ptr_2->ts.type == BT_VOID) + return true; + + if (c_ptr_2->ts.type != BT_DERIVED || c_ptr_2->ts.u.derived->from_intmod != INTMOD_ISO_C_BINDING || (c_ptr_1->ts.u.derived->intmod_sym_id - != c_ptr_2->ts.u.derived->intmod_sym_id))) - { - gfc_error ("Argument C_PTR_2 at %L to C_ASSOCIATED shall have the " - "same type as C_PTR_1: %s instead of %s", &c_ptr_1->where, - gfc_typename (&c_ptr_1->ts), - gfc_typename (&c_ptr_2->ts)); - return false; + != c_ptr_2->ts.u.derived->intmod_sym_id)) + { + gfc_error ("Argument C_PTR_2 at %L to C_ASSOCIATED shall have the " + "same type as C_PTR_1: %s instead of %s", &c_ptr_1->where, + gfc_typename (&c_ptr_1->ts), gfc_typename (&c_ptr_2->ts)); + return false; + } } if (c_ptr_2 && !scalar_check (c_ptr_2, 1)) diff --git a/gcc/fortran/interface.cc b/gcc/fortran/interface.cc index 1e552a3..753f589 100644 --- a/gcc/fortran/interface.cc +++ b/gcc/fortran/interface.cc @@ -1403,77 +1403,82 @@ gfc_check_dummy_characteristics (gfc_symbol *s1, gfc_symbol *s2, } } - /* Check INTENT. */ - if (s1->attr.intent != s2->attr.intent && !s1->attr.artificial - && !s2->attr.artificial) - { - snprintf (errmsg, err_len, "INTENT mismatch in argument '%s'", - s1->name); - return false; - } + /* A lot of information is missing for artificially generated + formal arguments, let's not look into that. */ - /* Check OPTIONAL attribute. */ - if (s1->attr.optional != s2->attr.optional) + if (!s1->attr.artificial && !s2->attr.artificial) { - snprintf (errmsg, err_len, "OPTIONAL mismatch in argument '%s'", - s1->name); - return false; - } + /* Check INTENT. */ + if (s1->attr.intent != s2->attr.intent) + { + snprintf (errmsg, err_len, "INTENT mismatch in argument '%s'", + s1->name); + return false; + } - /* Check ALLOCATABLE attribute. */ - if (s1->attr.allocatable != s2->attr.allocatable) - { - snprintf (errmsg, err_len, "ALLOCATABLE mismatch in argument '%s'", - s1->name); - return false; - } + /* Check OPTIONAL attribute. */ + if (s1->attr.optional != s2->attr.optional) + { + snprintf (errmsg, err_len, "OPTIONAL mismatch in argument '%s'", + s1->name); + return false; + } - /* Check POINTER attribute. */ - if (s1->attr.pointer != s2->attr.pointer) - { - snprintf (errmsg, err_len, "POINTER mismatch in argument '%s'", - s1->name); - return false; - } + /* Check ALLOCATABLE attribute. */ + if (s1->attr.allocatable != s2->attr.allocatable) + { + snprintf (errmsg, err_len, "ALLOCATABLE mismatch in argument '%s'", + s1->name); + return false; + } - /* Check TARGET attribute. */ - if (s1->attr.target != s2->attr.target) - { - snprintf (errmsg, err_len, "TARGET mismatch in argument '%s'", - s1->name); - return false; - } + /* Check POINTER attribute. */ + if (s1->attr.pointer != s2->attr.pointer) + { + snprintf (errmsg, err_len, "POINTER mismatch in argument '%s'", + s1->name); + return false; + } - /* Check ASYNCHRONOUS attribute. */ - if (s1->attr.asynchronous != s2->attr.asynchronous) - { - snprintf (errmsg, err_len, "ASYNCHRONOUS mismatch in argument '%s'", - s1->name); - return false; - } + /* Check TARGET attribute. */ + if (s1->attr.target != s2->attr.target) + { + snprintf (errmsg, err_len, "TARGET mismatch in argument '%s'", + s1->name); + return false; + } - /* Check CONTIGUOUS attribute. */ - if (s1->attr.contiguous != s2->attr.contiguous) - { - snprintf (errmsg, err_len, "CONTIGUOUS mismatch in argument '%s'", - s1->name); - return false; - } + /* Check ASYNCHRONOUS attribute. */ + if (s1->attr.asynchronous != s2->attr.asynchronous) + { + snprintf (errmsg, err_len, "ASYNCHRONOUS mismatch in argument '%s'", + s1->name); + return false; + } - /* Check VALUE attribute. */ - if (s1->attr.value != s2->attr.value) - { - snprintf (errmsg, err_len, "VALUE mismatch in argument '%s'", - s1->name); - return false; - } + /* Check CONTIGUOUS attribute. */ + if (s1->attr.contiguous != s2->attr.contiguous) + { + snprintf (errmsg, err_len, "CONTIGUOUS mismatch in argument '%s'", + s1->name); + return false; + } - /* Check VOLATILE attribute. */ - if (s1->attr.volatile_ != s2->attr.volatile_) - { - snprintf (errmsg, err_len, "VOLATILE mismatch in argument '%s'", - s1->name); - return false; + /* Check VALUE attribute. */ + if (s1->attr.value != s2->attr.value) + { + snprintf (errmsg, err_len, "VALUE mismatch in argument '%s'", + s1->name); + return false; + } + + /* Check VOLATILE attribute. */ + if (s1->attr.volatile_ != s2->attr.volatile_) + { + snprintf (errmsg, err_len, "VOLATILE mismatch in argument '%s'", + s1->name); + return false; + } } /* Check interface of dummy procedures. */ @@ -5849,6 +5854,12 @@ gfc_get_formal_from_actual_arglist (gfc_symbol *sym, char name[GFC_MAX_SYMBOL_LEN + 1]; static int var_num; + /* Do not infer the formal from actual arguments if we are dealing with + classes. */ + + if (sym->ts.type == BT_CLASS) + return; + f = &sym->formal; for (a = actual_args; a != NULL; a = a->next) { diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc index 94d5a1e..5884b79 100644 --- a/gcc/gimple-fold.cc +++ b/gcc/gimple-fold.cc @@ -8334,6 +8334,8 @@ fold_truth_andor_for_ifcombine (enum tree_code code, tree truth_type, ll_and_mask &= sign; if (l_xor) { + if (ll_bitsize != lr_bitsize) + return 0; if (!lr_and_mask.get_precision ()) lr_and_mask = sign; else @@ -8355,6 +8357,8 @@ fold_truth_andor_for_ifcombine (enum tree_code code, tree truth_type, rl_and_mask &= sign; if (r_xor) { + if (rl_bitsize != rr_bitsize) + return 0; if (!rr_and_mask.get_precision ()) rr_and_mask = sign; else @@ -8762,7 +8766,7 @@ fold_truth_andor_for_ifcombine (enum tree_code code, tree truth_type, wide_int lr_mask, rr_mask; if (lr_and_mask.get_precision ()) lr_mask = wi::lshift (wide_int::from (lr_and_mask, rnprec, UNSIGNED), - xlr_bitpos); + xlr_bitpos); else lr_mask = wi::shifted_mask (xlr_bitpos, lr_bitsize, false, rnprec); if (rr_and_mask.get_precision ()) diff --git a/gcc/ipa-inline-transform.cc b/gcc/ipa-inline-transform.cc index e00887b..46b8e5b 100644 --- a/gcc/ipa-inline-transform.cc +++ b/gcc/ipa-inline-transform.cc @@ -225,7 +225,7 @@ clone_inlined_nodes (struct cgraph_edge *e, bool duplicate, e->count, update_original, vNULL, true, inlining_into, - NULL); + NULL, NULL); n->used_as_abstract_origin = e->callee->used_as_abstract_origin; e->redirect_callee (n); } diff --git a/gcc/ipa-inline.cc b/gcc/ipa-inline.cc index 38fdbfd..35e5496 100644 --- a/gcc/ipa-inline.cc +++ b/gcc/ipa-inline.cc @@ -1865,7 +1865,7 @@ recursive_inlining (struct cgraph_edge *edge, { /* We need original clone to copy around. */ master_clone = node->create_clone (node->decl, node->count, - false, vNULL, true, NULL, NULL); + false, vNULL, true, NULL, NULL, NULL); for (e = master_clone->callees; e; e = e->next_callee) if (!e->inline_failed) clone_inlined_nodes (e, true, false, NULL); diff --git a/gcc/json.cc b/gcc/json.cc index e66a7ae..f3f3645 100644 --- a/gcc/json.cc +++ b/gcc/json.cc @@ -74,6 +74,52 @@ print_escaped_json_string (pretty_printer *pp, pp_character (pp, '"'); } +/* class pointer::token. */ + +pointer::token::token () +{ + m_parent = nullptr; + m_data.u_member = nullptr; + m_kind = kind::root_value; +} + +pointer::token::token (json::object &parent, const char *member) +{ + m_parent = &parent; + m_data.u_member = xstrdup (member); // ideally we'd share + m_kind = kind::object_member; +} + +pointer::token::token (json::array &parent, size_t index) +{ + m_parent = &parent; + m_data.u_index = index; + m_kind = kind::array_index; +} + +pointer::token::~token () +{ + if (m_kind == kind::object_member) + { + gcc_assert (m_data.u_member); + free (m_data.u_member); + } +} + +pointer::token & +pointer::token::operator= (pointer::token &&other) +{ + m_parent = other.m_parent; + m_data = other.m_data; + m_kind = other.m_kind; + + other.m_parent = nullptr; + other.m_data.u_member = nullptr; + other.m_kind = kind::root_value; + + return *this; +} + /* class json::value. */ /* Dump this json::value tree to OUTF. @@ -100,6 +146,94 @@ value::dump () const fprintf (stderr, "\n"); } +/* A deterministic total ordering for comparing json values, so that we + can e.g. put them in std::map. + + This is intended to follow the condition for equality described in + the JSON Schema standard (§4.3, “Instance equality”), as referenced + by SARIF v2.1.0 (§3.7.3 "Array properties with unique values"), but has + the following limitations: + - numbers are supposed to be checked for "the same mathematical value", + but in this implementation int vs float numbers won't compare as equal, + and float number comparison is bitwise + - strings are supposed to be "the same codepoint-for-codepoint", but + this implementation doesn't take into account canonicalization issues. */ + +int +value::compare (const value &val_a, const value &val_b) +{ + enum kind kind_a = val_a.get_kind (); + enum kind kind_b = val_b.get_kind (); + if (kind_a != kind_b) + return (int)kind_a - (int)kind_b; + + switch (kind_a) + { + default: + gcc_unreachable (); + + case JSON_OBJECT: + { + const object &obj_a = (const object &)val_a; + const object &obj_b = (const object &)val_b; + return object::compare (obj_a, obj_b); + } + break; + + case JSON_ARRAY: + { + const array &arr_a = (const array &)val_a; + const array &arr_b = (const array &)val_b; + if (int cmp_size = (int)arr_a.size () - (int)arr_b.size ()) + return cmp_size; + for (size_t idx = 0; idx < arr_a.size (); ++idx) + if (int cmp_element = compare (*arr_a[idx], *arr_b[idx])) + return cmp_element; + return 0; + } + break; + + case JSON_INTEGER: + { + const integer_number &int_a = (const integer_number &)val_a; + const integer_number &int_b = (const integer_number &)val_b; + return int_a.get () - int_b.get (); + } + break; + + case JSON_FLOAT: + { + const float_number &float_a = (const float_number &)val_a; + const float_number &float_b = (const float_number &)val_b; + union u + { + double u_double; + char u_buf[sizeof(double)]; + }; + union u u_a, u_b; + u_a.u_double = float_a.get (); + u_b.u_double = float_b.get (); + return memcmp (&u_a, &u_b, sizeof(double)); + } + break; + + case JSON_STRING: + { + const string &str_a = (const string &)val_a; + const string &str_b = (const string &)val_b; + return strcmp (str_a.get_string (), str_b.get_string ()); + } + break; + + case JSON_TRUE: + case JSON_FALSE: + case JSON_NULL: + /* All instances of literals compare equal to instances + of the same literal. */ + return 0; + } +} + /* class json::object, a subclass of json::value, representing an ordered collection of key/value pairs. */ @@ -180,6 +314,8 @@ object::set (const char *key, value *v) m_map.put (owned_key, v); m_keys.safe_push (owned_key); } + + v->m_pointer_token = pointer::token (*this, key); } /* Get the json::value * for KEY. @@ -234,6 +370,36 @@ object::set_bool (const char *key, bool v) set (key, new json::literal (v)); } +/* Subroutine of json::compare for comparing a pairs of objects. */ + +int +object::compare (const json::object &obj_a, const json::object &obj_b) +{ + if (int cmp_size = (int)obj_a.m_keys.length () - (int)obj_b.m_keys.length ()) + return cmp_size; + + for (auto iter_a : obj_a.m_map) + { + const char *key = iter_a.first; + const value *value_a = iter_a.second; + gcc_assert (value_a); + + const value *value_b = obj_b.get (key); + if (!value_b) + /* Key is in OBJ_A but not in OBJ_B. */ + return 1; + /* If key is OBJ_B but not in OBJ_A, then the + count of keys will have been different, or + OBJ_A would have had a key not in OBJ_B. */ + if (int cmp_value = value::compare (*value_a, *value_b)) + /* Values for key are non-equal. */ + return cmp_value; + } + + /* Objects are equal. */ + return 0; +} + /* class json::array, a subclass of json::value, representing an ordered collection of values. */ @@ -283,6 +449,7 @@ void array::append (value *v) { gcc_assert (v); + v->m_pointer_token = pointer::token (*this, m_elements.length ()); m_elements.safe_push (v); } @@ -540,6 +707,213 @@ test_formatting () " \"int\": 1776}, \"int\": 42}")); } +/* Helper function for reporting failure of JSON comparisons. */ + +static void +fail_comparison (const location &loc, + const char *desc, + const value &val_a, const value &val_b, + const char *desc_expected_value, + int actual_value) +{ + fprintf (stderr, "val_a: "); + val_a.dump (); + + fprintf (stderr, "val_b: "); + val_b.dump (); + + selftest::fail_formatted (loc, + "%s: failed JSON comparison:" + " expected: %s got: %i\n", + desc, + desc_expected_value, actual_value); +} + +/* Implementation of ASSERT_JSON_EQ. */ + +static void +assert_json_equal (const location &loc, + const char *desc, + const value &val_a, const value &val_b) +{ + /* Comparison should return zero, both ways, indicating no differences. */ + const int a_vs_b = value::compare (val_a, val_b); + if (a_vs_b != 0) + fail_comparison (loc, desc, val_a, val_b, "zero", a_vs_b); + + const int b_vs_a = value::compare (val_b, val_a); + if (b_vs_a != 0) + fail_comparison (loc, desc, val_b, val_a, "zero", b_vs_a); +} + +/* Verify that json::value::compare returns 0 ("no differences") on + VAL1 and VAL2, in both orders. */ + +#define ASSERT_JSON_EQ(VAL1, VAL2) \ + SELFTEST_BEGIN_STMT \ + assert_json_equal ((SELFTEST_LOCATION), \ + "ASSERT_JSON_EQ", \ + (VAL1), (VAL2)); \ + SELFTEST_END_STMT + +/* Implementation of ASSERT_JSON_NE. */ + +static void +assert_json_non_equal (const location &loc, + const char *desc, + const value &val_a, const value &val_b) +{ + /* Comparison should be non-zero, indicating differences. */ + const int a_vs_b = value::compare (val_a, val_b); + if (a_vs_b == 0) + fail_comparison (loc, desc, val_a, val_b, "non-zero", a_vs_b); + + const int b_vs_a = value::compare (val_b, val_a); + ASSERT_NE_AT (loc, b_vs_a, 0); + if (b_vs_a == 0) + fail_comparison (loc, desc, val_b, val_a, "non-zero", b_vs_a); + + /* Swapping the args should swap the sign of the result + (but isn't necessarily the negation). */ + if ( (a_vs_b > 0) == (b_vs_a > 0) ) + fail_comparison (loc, desc, val_b, val_a, "opposite signs", 1); +} + +/* Verify that json::value::compare returns non-zero ("different") on + VAL1 and VAL2, in both orders, and that they have opposite + sign. */ + +#define ASSERT_JSON_NE(VAL1, VAL2) \ + SELFTEST_BEGIN_STMT \ + assert_json_non_equal ((SELFTEST_LOCATION), \ + "ASSERT_JSON_NE", \ + (VAL1), (VAL2)); \ + SELFTEST_END_STMT + +/* Verify that json::value::compare works as expected. */ + +static void +test_comparisons () +{ + /* Literals. */ + + literal null_lit (JSON_NULL); + ASSERT_JSON_EQ (null_lit, null_lit); + + literal other_null_lit (JSON_NULL); + ASSERT_JSON_EQ (null_lit, other_null_lit); + + literal true_lit (JSON_TRUE); + ASSERT_JSON_EQ (true_lit, true_lit); + ASSERT_JSON_NE (true_lit, null_lit); + + literal false_lit (JSON_FALSE); + ASSERT_JSON_EQ (false_lit, false_lit); + ASSERT_JSON_NE (false_lit, true_lit); + ASSERT_JSON_NE (false_lit, null_lit); + + /* Strings. */ + string str_foo_1 ("foo"); + ASSERT_JSON_EQ (str_foo_1, str_foo_1); + + string str_foo_2 ("foo"); + ASSERT_JSON_EQ (str_foo_1, str_foo_2); + + string str_bar ("bar"); + ASSERT_JSON_NE (str_bar, str_foo_1); + + /* Numbers. */ + integer_number i_42 (42); + ASSERT_JSON_EQ (i_42, i_42); + integer_number i_42_2 (42); + ASSERT_JSON_EQ (i_42, i_42_2); + integer_number i_43 (43); + ASSERT_JSON_NE (i_42, i_43); + + float_number f_zero (0.0); + ASSERT_JSON_EQ (f_zero, f_zero); + float_number f_zero_2 (0.0); + ASSERT_JSON_EQ (f_zero, f_zero_2); + float_number f_one (1.0); + ASSERT_JSON_NE (f_zero, f_one); + /* We don't yet test the more awkward cases e.g. NaN. */ + + /* Objects. */ + + // Empty object + // Self comparison should be 0 + object empty_obj_a; + ASSERT_JSON_EQ (empty_obj_a, empty_obj_a); + + // Instances of empty objects should compare equal to each other + object empty_obj_b; + ASSERT_JSON_EQ (empty_obj_a, empty_obj_b); + + // Object with one field: + object obj_1; + obj_1.set_string ("foo", "bar"); + // Self comparison should be 0 + ASSERT_JSON_EQ (obj_1, obj_1); + + // but should be different to an empty object: + ASSERT_JSON_NE (obj_1, empty_obj_a); + + // Another with one field, with same key/value: + object obj_2; + obj_2.set_string ("foo", "bar"); + ASSERT_JSON_EQ (obj_1, obj_2); + + // Same key, different value: + object obj_3; + obj_3.set_string ("foo", "baz"); + ASSERT_JSON_NE (obj_1, obj_3); + + // Adding an extra property: + obj_2.set_integer ("year", 1066); + ASSERT_JSON_NE (obj_1, obj_2); + + /* Different insertion order, but same k-v pairs should be equal, + despite having different serialization. */ + object obj_4; + obj_4.set_integer ("year", 1066); + obj_4.set_string ("foo", "bar"); + ASSERT_JSON_EQ (obj_2, obj_4); + ASSERT_PRINT_EQ (obj_2, false, "{\"foo\": \"bar\", \"year\": 1066}"); + ASSERT_PRINT_EQ (obj_4, false, "{\"year\": 1066, \"foo\": \"bar\"}"); + + /* Arrays. */ + + // Empty array + array empty_arr_a; + // Self comparison should be 0 + ASSERT_JSON_EQ (empty_arr_a, empty_arr_a); + + // Objects and arrays are different + ASSERT_JSON_NE (empty_obj_a, empty_arr_a); + + // Instances of empty arrays should compare equal to each other + array empty_arr_b; + ASSERT_JSON_EQ (empty_arr_a, empty_arr_b); + + // Array with one element: + array arr_1; + arr_1.append (std::make_unique<string> ("foo")); + // Self comparison should be 0 + ASSERT_JSON_EQ (arr_1, arr_1); + + // but should be different to an empty array: + ASSERT_JSON_NE (arr_1, empty_arr_a); + + // Another with one element: + array arr_2; + arr_2.append (std::make_unique<string> ("foo")); + ASSERT_JSON_EQ (arr_1, arr_2); + + // Adding an extra element: + arr_2.append (std::make_unique<string> ("bar")); + ASSERT_JSON_NE (arr_1, arr_2); +} + /* Run all of the selftests within this file. */ void @@ -553,6 +927,7 @@ json_cc_tests () test_writing_strings (); test_writing_literals (); test_formatting (); + test_comparisons (); } } // namespace selftest @@ -73,6 +73,49 @@ enum kind JSON_NULL }; +namespace pointer { // json::pointer + +/* Implementation of JSON pointer (RFC 6901). */ + +/* A token within a JSON pointer, expressing the parent of a particular + JSON value, and how it is descended from that parent. + + A JSON pointer can be built as a list of these tokens. */ + +struct token +{ + enum class kind + { + root_value, + object_member, + array_index + }; + + token (); + token (json::object &parent, const char *member); + token (json::array &parent, size_t index); + token (const token &other) = delete; + token (token &&other) = delete; + + ~token (); + + token & + operator= (const token &other) = delete; + + token & + operator= (token &&other); + + json::value *m_parent; + union u + { + char *u_member; + size_t u_index; + } m_data; + enum kind m_kind; +}; + +} // namespace json::pointer + /* Base class of JSON value. */ class value @@ -84,6 +127,14 @@ class value void dump (FILE *, bool formatted) const; void DEBUG_FUNCTION dump () const; + + virtual object *dyn_cast_object () { return nullptr; } + + static int compare (const json::value &val_a, const json::value &val_b); + + const pointer::token &get_pointer_token () const { return m_pointer_token; } + + pointer::token m_pointer_token; }; /* Subclass of value for objects: a collection of key/value pairs @@ -100,6 +151,8 @@ class object : public value enum kind get_kind () const final override { return JSON_OBJECT; } void print (pretty_printer *pp, bool formatted) const final override; + object *dyn_cast_object () final override { return this; } + bool is_empty () const { return m_map.is_empty (); } void set (const char *key, value *v); @@ -127,6 +180,8 @@ class object : public value /* Set to literal true/false. */ void set_bool (const char *key, bool v); + static int compare (const json::object &obj_a, const json::object &obj_b); + private: typedef hash_map <char *, value *, simple_hashmap_traits<nofree_string_hash, value *> > map_t; diff --git a/gcc/lazy-diagnostic-path.cc b/gcc/lazy-diagnostic-path.cc index 1474f70..37c8e25 100644 --- a/gcc/lazy-diagnostic-path.cc +++ b/gcc/lazy-diagnostic-path.cc @@ -91,7 +91,8 @@ class test_lazy_path : public lazy_diagnostic_path { public: test_lazy_path (pretty_printer &pp) - : m_pp (pp) + : lazy_diagnostic_path (m_logical_loc_mgr), + m_pp (pp) { } std::unique_ptr<diagnostic_path> make_inner_path () const final override @@ -99,12 +100,15 @@ public: tree fntype_void_void = build_function_type_array (void_type_node, 0, NULL); tree fndecl_foo = build_fn_decl ("foo", fntype_void_void); - auto path = std::make_unique<simple_diagnostic_path> (&m_pp); + auto path + = std::make_unique<simple_diagnostic_path> (m_logical_loc_mgr, + &m_pp); path->add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "first %qs", "free"); path->add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "double %qs", "free"); return path; } private: + const tree_logical_location_manager m_logical_loc_mgr; pretty_printer &m_pp; }; diff --git a/gcc/lazy-diagnostic-path.h b/gcc/lazy-diagnostic-path.h index 0156456..9609cab 100644 --- a/gcc/lazy-diagnostic-path.h +++ b/gcc/lazy-diagnostic-path.h @@ -48,6 +48,12 @@ class lazy_diagnostic_path : public diagnostic_path bool generated_p () const { return m_inner_path != nullptr; } +protected: + lazy_diagnostic_path (const logical_location_manager &logical_loc_mgr) + : diagnostic_path (logical_loc_mgr) + { + } + private: void lazily_generate_path () const; virtual std::unique_ptr<diagnostic_path> make_inner_path () const = 0; diff --git a/gcc/libgdiagnostics++.h b/gcc/libgdiagnostics++.h index 39477a0..18a88a2 100644 --- a/gcc/libgdiagnostics++.h +++ b/gcc/libgdiagnostics++.h @@ -109,6 +109,25 @@ public: : m_inner (logical_loc) {} + operator bool() { return m_inner != nullptr; } + + // Various accessors + enum diagnostic_logical_location_kind_t get_kind () const; + logical_location get_parent () const; + const char *get_short_name () const; + const char *get_fully_qualified_name () const; + const char *get_decorated_name () const; + + bool operator== (const logical_location &other) const + { + return m_inner == other.m_inner; + } + + bool operator!= (const logical_location &other) const + { + return m_inner != other.m_inner; + } + const diagnostic_logical_location *m_inner; }; @@ -385,6 +404,51 @@ physical_location::get_file () const return file (diagnostic_physical_location_get_file (m_inner)); } +// class logical_location + +inline enum diagnostic_logical_location_kind_t +logical_location::get_kind () const +{ + // m_inner must be non-null + return diagnostic_logical_location_get_kind (m_inner); +} + +inline logical_location +logical_location::get_parent () const +{ + if (m_inner) + return diagnostic_logical_location_get_parent (m_inner); + else + return nullptr; +} + +inline const char * +logical_location::get_short_name () const +{ + if (m_inner) + return diagnostic_logical_location_get_short_name (m_inner); + else + return nullptr; +} + +inline const char * +logical_location::get_fully_qualified_name () const +{ + if (m_inner) + return diagnostic_logical_location_get_fully_qualified_name (m_inner); + else + return nullptr; +} + +inline const char * +logical_location::get_decorated_name () const +{ + if (m_inner) + return diagnostic_logical_location_get_decorated_name (m_inner); + else + return nullptr; +} + // class execution_path inline diagnostic_event_id diff --git a/gcc/libgdiagnostics.cc b/gcc/libgdiagnostics.cc index c2eb975..70b0f36 100644 --- a/gcc/libgdiagnostics.cc +++ b/gcc/libgdiagnostics.cc @@ -162,7 +162,7 @@ as_location_t (const diagnostic_physical_location *loc) /* This has to be a "struct" as it is exposed in the C API. */ -struct diagnostic_logical_location : public logical_location +struct diagnostic_logical_location { diagnostic_logical_location (enum diagnostic_logical_location_kind_t kind, const diagnostic_logical_location *parent, @@ -177,55 +177,6 @@ struct diagnostic_logical_location : public logical_location { } - const char *get_short_name () const final override - { - return m_short_name.get_str (); - } - const char *get_name_with_scope () const final override - { - return m_fully_qualified_name.get_str (); - } - const char *get_internal_name () const final override - { - return m_decorated_name.get_str (); - } - enum logical_location_kind get_kind () const final override - { - switch (m_kind) - { - default: - gcc_unreachable (); - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION: - return LOGICAL_LOCATION_KIND_FUNCTION; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER: - return LOGICAL_LOCATION_KIND_MEMBER; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE: - return LOGICAL_LOCATION_KIND_MODULE; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE: - return LOGICAL_LOCATION_KIND_NAMESPACE; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE: - return LOGICAL_LOCATION_KIND_TYPE; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE: - return LOGICAL_LOCATION_KIND_RETURN_TYPE; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER: - return LOGICAL_LOCATION_KIND_PARAMETER; - case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE: - return LOGICAL_LOCATION_KIND_VARIABLE; - } - } - - enum diagnostic_logical_location_kind_t get_external_kind () const - { - return m_kind; - } - - const diagnostic_logical_location *get_parent () const { return m_parent; } - - label_text get_name_for_path_output () const - { - return label_text::borrow (m_short_name.get_str ()); - } - bool operator< (const diagnostic_logical_location &other) const { @@ -243,7 +194,6 @@ struct diagnostic_logical_location : public logical_location return false; } -private: enum diagnostic_logical_location_kind_t m_kind; const diagnostic_logical_location *m_parent; owned_nullable_string m_short_name; @@ -308,6 +258,112 @@ round_alloc_size (size_t s) return s; } +class impl_logical_location_manager : public logical_location_manager +{ +public: + static const diagnostic_logical_location * + ptr_from_key (logical_location k) + { + return k.cast_to<const diagnostic_logical_location *> (); + } + + static logical_location + key_from_ptr (const diagnostic_logical_location *ptr) + { + return logical_location::from_ptr (ptr); + } + + const char *get_short_name (key k) const final override + { + if (auto loc = ptr_from_key (k)) + return loc->m_short_name.get_str (); + else + return nullptr; + } + + const char *get_name_with_scope (key k) const final override + { + if (auto loc = ptr_from_key (k)) + return loc->m_fully_qualified_name.get_str (); + else + return nullptr; + } + + const char *get_internal_name (key k) const final override + { + if (auto loc = ptr_from_key (k)) + return loc->m_decorated_name.get_str (); + else + return nullptr; + } + + enum logical_location_kind get_kind (key k) const final override + { + auto loc = ptr_from_key (k); + gcc_assert (loc); + switch (loc->m_kind) + { + default: + gcc_unreachable (); + + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION: + return LOGICAL_LOCATION_KIND_FUNCTION; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER: + return LOGICAL_LOCATION_KIND_MEMBER; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE: + return LOGICAL_LOCATION_KIND_MODULE; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE: + return LOGICAL_LOCATION_KIND_NAMESPACE; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE: + return LOGICAL_LOCATION_KIND_TYPE; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE: + return LOGICAL_LOCATION_KIND_RETURN_TYPE; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER: + return LOGICAL_LOCATION_KIND_PARAMETER; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE: + return LOGICAL_LOCATION_KIND_VARIABLE; + + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_ELEMENT: + return LOGICAL_LOCATION_KIND_ELEMENT; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_ATTRIBUTE: + return LOGICAL_LOCATION_KIND_ATTRIBUTE; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_TEXT: + return LOGICAL_LOCATION_KIND_TEXT; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_COMMENT: + return LOGICAL_LOCATION_KIND_COMMENT; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION: + return LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_DTD: + return LOGICAL_LOCATION_KIND_DTD; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_DECLARATION: + return LOGICAL_LOCATION_KIND_DECLARATION; + + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT: + return LOGICAL_LOCATION_KIND_OBJECT; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY: + return LOGICAL_LOCATION_KIND_ARRAY; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY: + return LOGICAL_LOCATION_KIND_PROPERTY; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE: + return LOGICAL_LOCATION_KIND_VALUE; + } + } + + label_text get_name_for_path_output (key k) const final override + { + auto loc = ptr_from_key (k); + gcc_assert (loc); + return label_text::borrow (loc->m_short_name.get_str ()); + } + + key get_parent (key k) const final override + { + auto loc = ptr_from_key (k); + gcc_assert (loc); + return key_from_ptr (loc->m_parent); + } +}; + class impl_diagnostic_client_data_hooks : public diagnostic_client_data_hooks { public: @@ -316,7 +372,14 @@ public: {} const client_version_info *get_any_version_info () const final override; - const logical_location *get_current_logical_location () const final override; + + const logical_location_manager * + get_logical_location_manager () const final override + { + return &m_logical_location_manager; + } + logical_location get_current_logical_location () const final override; + const char * maybe_get_sarif_source_language (const char *filename) const final override; void add_sarif_invocation_properties (sarif_object &invocation_obj) @@ -324,6 +387,7 @@ public: private: diagnostic_manager &m_mgr; + impl_logical_location_manager m_logical_location_manager; }; class impl_client_version_info : public client_version_info @@ -424,6 +488,14 @@ public: line_maps *get_line_table () { return &m_line_table; } diagnostic_context &get_dc () { return m_dc; } + const logical_location_manager & + get_logical_location_manager () const + { + auto mgr = m_dc.get_logical_location_manager (); + gcc_assert (mgr); + return *mgr; + } + void write_patch (FILE *dst_stream); void add_sink (std::unique_ptr<sink> sink) @@ -706,9 +778,9 @@ public: pp_string (&pp, m_desc_uncolored.get ()); } - const logical_location *get_logical_location () const + logical_location get_logical_location () const final override { - return m_logical_loc; + return impl_logical_location_manager::key_from_ptr (m_logical_loc); } meaning get_meaning () const final override @@ -772,8 +844,9 @@ private: struct diagnostic_execution_path : public diagnostic_path { - diagnostic_execution_path () - : m_thread ("") + diagnostic_execution_path (const logical_location_manager &logical_loc_mgr) + : diagnostic_path (logical_loc_mgr), + m_thread ("") { } @@ -814,9 +887,9 @@ struct diagnostic_execution_path : public diagnostic_path same_function_p (int event_idx_a, int event_idx_b) const final override { - const logical_location *logical_loc_a + logical_location logical_loc_a = m_events[event_idx_a]->get_logical_location (); - const logical_location *logical_loc_b + logical_location logical_loc_b = m_events[event_idx_b]->get_logical_location (); /* Pointer equality, as we uniqify logical location instances. */ @@ -902,7 +975,9 @@ public: diagnostic_execution_path * add_execution_path () { - m_path = std::make_unique<diagnostic_execution_path> (); + m_path + = std::make_unique<diagnostic_execution_path> + (m_diag_mgr.get_logical_location_manager ()); m_rich_loc.set_path (m_path.get ()); return m_path.get (); } @@ -973,12 +1048,13 @@ impl_diagnostic_client_data_hooks::get_any_version_info () const return m_mgr.get_client_version_info (); } -const logical_location * +logical_location impl_diagnostic_client_data_hooks::get_current_logical_location () const { gcc_assert (m_mgr.get_current_diag ()); - return m_mgr.get_current_diag ()->get_logical_location (); + return impl_logical_location_manager::key_from_ptr + (m_mgr.get_current_diag ()->get_logical_location ()); } const char * @@ -1046,21 +1122,57 @@ diagnostic_text_sink::text_starter (diagnostic_text_output_format &text_output, if (diag_logical_loc && diag_logical_loc != mgr.get_prev_diag_logical_loc ()) { pp_set_prefix (pp, nullptr); - switch (diag_logical_loc->get_kind ()) + + /* This macro is used to ensure that all format strings are visible to gettext + and checked at compile time. */ + +#define CASE(KIND, MSGID) \ + case KIND: \ + if (const char *name \ + = diag_logical_loc->m_fully_qualified_name.get_str ()) \ + { \ + pp_printf (pp, (MSGID), name); \ + pp_character (pp, ':'); \ + pp_newline (pp); \ + } \ + break; + + switch (diag_logical_loc->m_kind) { default: break; - case LOGICAL_LOCATION_KIND_FUNCTION: - if (const char *name - = diag_logical_loc->get_name_with_scope ()) - { - pp_printf (pp, _("In function %qs"), name); - pp_character (pp, ':'); - pp_newline (pp); - } - break; - // TODO: handle other cases + + /* Kinds within executable code. */ + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION, _("In function %qs")) + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER, _("In member %qs")) + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE, _("In module %qs")) + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE, _("In namespace %qs")) + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE, _("In type %qs")) + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE, + _("In return type %qs")) + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER, _("In parameter %qs")) + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE, _("In variable %qs")) + + /* Kinds within XML or HTML documents. */ + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_ELEMENT, _("In element %qs")) + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_ATTRIBUTE, _("In attribute %qs")) + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_TEXT, _("In text %qs")) + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_COMMENT, _("In comment %qs")) + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION, + _("In processing instruction %qs")) + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_DTD, _("In DTD %qs")) + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_DECLARATION, + _("In declaration %qs")) + + /* Kinds within JSON documents. */ + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT, _("In JSON object %qs")) + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY, _("In JSON array %qs")) + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY, + _("In JSON property %qs")) + CASE(DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE, _("In JSON value %qs")) } + +#undef CASE } pp_set_prefix (pp, text_output.build_prefix (*info)); @@ -1130,7 +1242,9 @@ GCC_DIAGNOSTIC_POP diagnostic_execution_path * diagnostic_manager::new_execution_path () { - return new diagnostic_execution_path (); + auto mgr = m_dc.get_logical_location_manager (); + gcc_assert (mgr); + return new diagnostic_execution_path (*mgr); } /* Error-checking at the API boundary. */ @@ -1458,10 +1572,11 @@ diagnostic_manager_debug_dump_logical_location (const diagnostic_manager *diag_m if (loc) { fprintf (out, "logical_location(kind="); - switch (loc->get_external_kind ()) + switch (loc->m_kind) { default: gcc_unreachable (); + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION: fprintf (out, "function"); break; @@ -1486,16 +1601,54 @@ diagnostic_manager_debug_dump_logical_location (const diagnostic_manager *diag_m case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE: fprintf (out, "variable"); break; + + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_ELEMENT: + fprintf (out, "element"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_ATTRIBUTE: + fprintf (out, "attribute"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_TEXT: + fprintf (out, "text"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_COMMENT: + fprintf (out, "comment"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION: + fprintf (out, "processing_instruction"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_DTD: + fprintf (out, "dtd"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_DECLARATION: + fprintf (out, "declaration"); + break; + + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT: + fprintf (out, "object"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY: + fprintf (out, "array"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY: + fprintf (out, "property"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE: + fprintf (out, "value"); + break; } - if (const diagnostic_logical_location *parent = loc->get_parent ()) - diagnostic_manager_debug_dump_logical_location (diag_mgr, - parent, - out); - if (const char *val = loc->get_short_name ()) + if (auto parent = loc->m_parent) + { + fprintf (out, ", parent="); + diagnostic_manager_debug_dump_logical_location (diag_mgr, + parent, + out); + } + if (const char *val = loc->m_short_name.get_str ()) fprintf (out, ", short_name=\"%s\"", val); - if (const char *val = loc->get_name_with_scope ()) + if (const char *val = loc->m_fully_qualified_name.get_str ()) fprintf (out, ", fully_qualified_name=\"%s\"", val); - if (const char *val = loc->get_internal_name ()) + if (const char *val = loc->m_decorated_name.get_str ()) fprintf (out, ", decorated_name=\"%s\"", val); fprintf (out, ")"); } @@ -1788,3 +1941,45 @@ diagnostic_physical_location_get_file (const diagnostic_physical_location *physi return physical_loc->get_file (); } + +/* Public entrypoints for accessing logical location data. */ + +enum diagnostic_logical_location_kind_t +diagnostic_logical_location_get_kind (const diagnostic_logical_location *loc) +{ + FAIL_IF_NULL (loc); + + return loc->m_kind; +} + +const diagnostic_logical_location * +diagnostic_logical_location_get_parent (const diagnostic_logical_location *loc) +{ + FAIL_IF_NULL (loc); + + return loc->m_parent; +} + +const char * +diagnostic_logical_location_get_short_name (const diagnostic_logical_location *loc) +{ + FAIL_IF_NULL (loc); + + return loc->m_short_name.get_str (); +} + +const char * +diagnostic_logical_location_get_fully_qualified_name (const diagnostic_logical_location *loc) +{ + FAIL_IF_NULL (loc); + + return loc->m_fully_qualified_name.get_str (); +} + +const char * +diagnostic_logical_location_get_decorated_name (const diagnostic_logical_location *loc) +{ + FAIL_IF_NULL (loc); + + return loc->m_decorated_name.get_str (); +} diff --git a/gcc/libgdiagnostics.h b/gcc/libgdiagnostics.h index 2ce0f4c..f957779 100644 --- a/gcc/libgdiagnostics.h +++ b/gcc/libgdiagnostics.h @@ -161,6 +161,7 @@ typedef struct diagnostic_logical_location diagnostic_logical_location; enum diagnostic_logical_location_kind_t { + /* Kinds within executable code. */ DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION, DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER, DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE, @@ -168,7 +169,22 @@ enum diagnostic_logical_location_kind_t DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE, DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE, DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER, - DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE + DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE, + + /* Kinds within XML or HTML documents. */ + DIAGNOSTIC_LOGICAL_LOCATION_KIND_ELEMENT, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_ATTRIBUTE, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_TEXT, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_COMMENT, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_DTD, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_DECLARATION, + + /* Kinds within JSON documents. */ + DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE }; /* A "diagnostic" is an opaque bundle of state for a particular @@ -487,6 +503,32 @@ diagnostic_manager_debug_dump_logical_location (const diagnostic_manager *diag_m LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2) LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3); +/* Accessors for logical locations (added in LIBGDIAGNOSTICS_ABI_1; + you can test for their presence using + #ifdef LIBDIAGNOSTICS_HAVE_LOGICAL_LOCATION_ACCESSORS +*/ +#define LIBDIAGNOSTICS_HAVE_LOGICAL_LOCATION_ACCESSORS + +extern enum diagnostic_logical_location_kind_t +diagnostic_logical_location_get_kind (const diagnostic_logical_location *loc) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +extern const diagnostic_logical_location * +diagnostic_logical_location_get_parent (const diagnostic_logical_location *loc) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +extern const char * +diagnostic_logical_location_get_short_name (const diagnostic_logical_location *loc) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +extern const char * +diagnostic_logical_location_get_fully_qualified_name (const diagnostic_logical_location *loc) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +extern const char * +diagnostic_logical_location_get_decorated_name (const diagnostic_logical_location *loc) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + /* Diagnostic groups. */ /* Begin a diagnostic group. All diagnostics emitted within diff --git a/gcc/libgdiagnostics.map b/gcc/libgdiagnostics.map index 5958cfe..4233cf8 100644 --- a/gcc/libgdiagnostics.map +++ b/gcc/libgdiagnostics.map @@ -73,3 +73,13 @@ LIBGDIAGNOSTICS_ABI_0 local: *; }; + +# Add accessors for diagnostic_logical_location. +LIBGDIAGNOSTICS_ABI_1 { + global: + diagnostic_logical_location_get_kind; + diagnostic_logical_location_get_parent; + diagnostic_logical_location_get_short_name; + diagnostic_logical_location_get_fully_qualified_name; + diagnostic_logical_location_get_decorated_name; +} LIBGDIAGNOSTICS_ABI_0; diff --git a/gcc/libsarifreplay.cc b/gcc/libsarifreplay.cc index f5f1f20..6c79762 100644 --- a/gcc/libsarifreplay.cc +++ b/gcc/libsarifreplay.cc @@ -106,6 +106,82 @@ make_physical_location (libgdiagnostics::manager &mgr, return mgr.new_location_from_range (start, start, end); } +static enum diagnostic_logical_location_kind_t +get_logical_location_kind_for_json_kind (enum json::kind json_kind) +{ + switch (json_kind) + { + default: + gcc_unreachable (); + + case json::JSON_OBJECT: + return DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT; + + case json::JSON_ARRAY: + return DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY; + + case json::JSON_INTEGER: + case json::JSON_FLOAT: + case json::JSON_STRING: + case json::JSON_TRUE: + case json::JSON_FALSE: + case json::JSON_NULL: + return DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY; + /* Perhaps this should be DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE, + but then we need to more carefully track context. */ + } +} + +static libgdiagnostics::logical_location +make_logical_location_from_jv (libgdiagnostics::manager &mgr, + const json::value &jv) +{ + libgdiagnostics::logical_location parent; + const json::pointer::token &pointer_token = jv.get_pointer_token (); + + if (pointer_token.m_parent) + /* Recursively ensure that we have ancestor locations. */ + parent = make_logical_location_from_jv (mgr, + *jv.m_pointer_token.m_parent); + + std::string short_name; + std::string fully_qualified_name; + switch (pointer_token.m_kind) + { + default: + gcc_unreachable (); + + case json::pointer::token::kind::root_value: + short_name = ""; + fully_qualified_name = ""; + break; + + case json::pointer::token::kind::object_member: + short_name = pointer_token.m_data.u_member; + gcc_assert (parent.m_inner); + fully_qualified_name + = std::string (parent.get_fully_qualified_name ()) + "/" + short_name; + break; + + case json::pointer::token::kind::array_index: + short_name = std::to_string (pointer_token.m_data.u_index); + gcc_assert (parent.m_inner); + fully_qualified_name + = std::string (parent.get_fully_qualified_name ()) + "/" + short_name; + break; + } + + enum diagnostic_logical_location_kind_t kind + = get_logical_location_kind_for_json_kind (jv.get_kind ()); + + return mgr.new_logical_location (kind, + parent, + short_name.c_str (), + fully_qualified_name.c_str (), + nullptr); +} + + enum class status { ok, @@ -417,6 +493,9 @@ private: m_json_location_map.get_range_for_value (jv)); diag.set_location (loc_range); + diag.set_logical_location (make_logical_location_from_jv (m_control_mgr, + jv)); + diag.finish_va (gmsgid, args); } @@ -2008,7 +2087,32 @@ handle_logical_location_object (const json::object &logical_loc_obj, { "parameter", DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER }, { "variable", - DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE } }; + DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE }, + + { "element", + DIAGNOSTIC_LOGICAL_LOCATION_KIND_ELEMENT }, + { "attribute", + DIAGNOSTIC_LOGICAL_LOCATION_KIND_ATTRIBUTE }, + { "text", + DIAGNOSTIC_LOGICAL_LOCATION_KIND_TEXT }, + { "comment", + DIAGNOSTIC_LOGICAL_LOCATION_KIND_COMMENT }, + { "processingInstruction", + DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION }, + { "dtd", + DIAGNOSTIC_LOGICAL_LOCATION_KIND_DTD }, + { "declaration", + DIAGNOSTIC_LOGICAL_LOCATION_KIND_DECLARATION }, + + { "object", + DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT }, + { "array", + DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY }, + { "property", + DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY }, + { "value", + DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE }, + }; auto result = get_value_from_json_string<enum diagnostic_logical_location_kind_t> (*kind_str, kind_prop, kind_values, ARRAY_SIZE (kind_values)); diff --git a/gcc/logical-location.h b/gcc/logical-location.h index 000335c..dba9dc4 100644 --- a/gcc/logical-location.h +++ b/gcc/logical-location.h @@ -33,6 +33,7 @@ enum logical_location_kind { LOGICAL_LOCATION_KIND_UNKNOWN, + /* Kinds within executable code. */ LOGICAL_LOCATION_KIND_FUNCTION, LOGICAL_LOCATION_KIND_MEMBER, LOGICAL_LOCATION_KIND_MODULE, @@ -40,41 +41,135 @@ enum logical_location_kind LOGICAL_LOCATION_KIND_TYPE, LOGICAL_LOCATION_KIND_RETURN_TYPE, LOGICAL_LOCATION_KIND_PARAMETER, - LOGICAL_LOCATION_KIND_VARIABLE + LOGICAL_LOCATION_KIND_VARIABLE, + + /* Kinds within XML or HTML documents. */ + LOGICAL_LOCATION_KIND_ELEMENT, + LOGICAL_LOCATION_KIND_ATTRIBUTE, + LOGICAL_LOCATION_KIND_TEXT, + LOGICAL_LOCATION_KIND_COMMENT, + LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION, + LOGICAL_LOCATION_KIND_DTD, + LOGICAL_LOCATION_KIND_DECLARATION, + + /* Kinds within JSON documents. */ + LOGICAL_LOCATION_KIND_OBJECT, + LOGICAL_LOCATION_KIND_ARRAY, + LOGICAL_LOCATION_KIND_PROPERTY, + LOGICAL_LOCATION_KIND_VALUE }; -/* Abstract base class for passing around logical locations in the +/* We want to efficiently support passing around logical locations in the diagnostics subsystem, such as: - "within function 'foo'", or - - "within method 'bar'", - but *without* requiring knowledge of trees - (see tree-logical-location.h for concrete subclasses relating to trees, - and selftest-logical-location.h for a concrete subclass for selftests). */ + - "within method 'bar'" -class logical_location + However we want to do this *without* requiring knowledge of trees (or of + libgdiagnostics internals), and without requiring heap allocation of an + interface class when emitting a diagnostic. + + To do this, we split the implementation into logical_location, which is + a wrapper around a (const void *), and logical_location_manager which + is provided by the client and has vfunc hooks for interpreting + logical_location instances. + + Every logical_location is associated with a logical_location_manager and + only has meaning in relation to that manager. + + A "nullptr" within a logical_location means "no logical location". + + See tree-logical-location.h for concrete subclasses relating to trees, + where the pointer is a const_tree. + + See selftest-logical-location.h for a concrete subclass for selftests. */ + +/* Abstract base class for giving meaning to logical_location values. + Typically there will just be one client-provided instance, of a + client-specific subclass. */ + +class logical_location_manager { public: - virtual ~logical_location () {} - - /* Get a string (or NULL) suitable for use by the SARIF logicalLocation + /* Extrinsic state for identifying a specific logical location. + This will be our logical_location type. + This only makes sense with respect to a specific manager. + e.g. for a tree-based one it's a wrapper around "tree". + "nullptr" means "no logical location". */ + class key + { + public: + key () : m_ptr (nullptr) {} + + static key from_ptr (const void *ptr) + { + return key (ptr); + } + + operator bool () const + { + return m_ptr != nullptr; + } + + template <typename T> + T cast_to () const { return static_cast<T> (m_ptr); } + + bool + operator== (const key &other) const + { + return m_ptr == other.m_ptr; + } + + bool + operator!= (const key &other) const + { + return m_ptr != other.m_ptr; + } + + bool + operator< (const key &other) const + { + return m_ptr < other.m_ptr; + } + + private: + explicit key (const void *ptr) : m_ptr (ptr) {} + + const void *m_ptr; + }; + + virtual ~logical_location_manager () {} + + /* vfuncs for interpreting logical_location values. */ + + /* Get a string (or NULL) for K suitable for use by the SARIF logicalLocation "name" property (SARIF v2.1.0 section 3.33.4). */ - virtual const char *get_short_name () const = 0; + virtual const char *get_short_name (key k) const = 0; - /* Get a string (or NULL) suitable for use by the SARIF logicalLocation + /* Get a string (or NULL) for K suitable for use by the SARIF logicalLocation "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */ - virtual const char *get_name_with_scope () const = 0; + virtual const char *get_name_with_scope (key k) const = 0; - /* Get a string (or NULL) suitable for use by the SARIF logicalLocation + /* Get a string (or NULL) for K suitable for use by the SARIF logicalLocation "decoratedName" property (SARIF v2.1.0 section 3.33.6). */ - virtual const char *get_internal_name () const = 0; + virtual const char *get_internal_name (key k) const = 0; + + /* Get what kind of SARIF logicalLocation K is (if any). */ + virtual enum logical_location_kind get_kind (key k) const = 0; - /* Get what kind of SARIF logicalLocation this is (if any). */ - virtual enum logical_location_kind get_kind () const = 0; + /* Get a string for location K in a form suitable for path output. */ + virtual label_text get_name_for_path_output (key k) const = 0; - /* Get a string for this location in a form suitable for path output. */ - virtual label_text get_name_for_path_output () const = 0; + /* Get the parent logical_logical of K, if any, or nullptr. */ + virtual key get_parent (key k) const = 0; - bool function_p () const; + bool function_p (key k) const; }; +/* A logical location is a key for a given logical_location_manager. + + Note that there is no integration with GCC's garbage collector and thus + logical_location instances can't be long-lived. */ + +typedef logical_location_manager::key logical_location; + #endif /* GCC_LOGICAL_LOCATION_H. */ diff --git a/gcc/lto-cgraph.cc b/gcc/lto-cgraph.cc index 8439c51..ec34f65 100644 --- a/gcc/lto-cgraph.cc +++ b/gcc/lto-cgraph.cc @@ -1303,7 +1303,7 @@ input_node (struct lto_file_decl_data *file_data, { node = dyn_cast<cgraph_node *> (nodes[clone_ref])->create_clone (fn_decl, profile_count::uninitialized (), false, - vNULL, false, NULL, NULL); + vNULL, false, NULL, NULL, NULL); } else { diff --git a/gcc/selftest-diagnostic-path.cc b/gcc/selftest-diagnostic-path.cc index 0e80eea..04372e7 100644 --- a/gcc/selftest-diagnostic-path.cc +++ b/gcc/selftest-diagnostic-path.cc @@ -36,7 +36,8 @@ namespace selftest { /* class test_diagnostic_path : public diagnostic_path. */ test_diagnostic_path::test_diagnostic_path (pretty_printer *event_pp) -: m_event_pp (event_pp) +: diagnostic_path (m_test_logical_loc_mgr), + m_event_pp (event_pp) { add_thread ("main"); } @@ -75,12 +76,8 @@ bool test_diagnostic_path::same_function_p (int event_idx_a, int event_idx_b) const { - const char *name_a = m_events[event_idx_a]->get_function_name (); - const char *name_b = m_events[event_idx_b]->get_function_name (); - - if (name_a && name_b) - return 0 == strcmp (name_a, name_b); - return name_a == name_b; + return (m_events[event_idx_a]->get_logical_location () + == m_events[event_idx_b]->get_logical_location ()); } diagnostic_thread_id_t @@ -121,7 +118,10 @@ test_diagnostic_path::add_event (location_t loc, va_end (ap); test_diagnostic_event *new_event - = new test_diagnostic_event (loc, funcname, depth, pp_formatted_text (pp)); + = new test_diagnostic_event (loc, + logical_location_from_funcname (funcname), + depth, + pp_formatted_text (pp)); m_events.safe_push (new_event); pp_clear_output_area (pp); @@ -154,8 +154,11 @@ test_diagnostic_path::add_thread_event (diagnostic_thread_id_t thread_id, va_end (ap); test_diagnostic_event *new_event - = new test_diagnostic_event (loc, funcname, depth, pp_formatted_text (pp), - thread_id); + = new test_diagnostic_event (loc, + logical_location_from_funcname (funcname), + depth, + pp_formatted_text (pp), + thread_id); m_events.safe_push (new_event); pp_clear_output_area (pp); @@ -203,18 +206,24 @@ test_diagnostic_path::add_call (const char *caller_name, add_entry (callee_name, caller_stack_depth + 1, thread_id); } +logical_location +test_diagnostic_path::logical_location_from_funcname (const char *funcname) +{ + return m_test_logical_loc_mgr.logical_location_from_funcname (funcname); +} + /* struct test_diagnostic_event. */ /* test_diagnostic_event's ctor. */ test_diagnostic_event:: test_diagnostic_event (location_t loc, - const char *funcname, - int depth, - const char *desc, - diagnostic_thread_id_t thread_id) + logical_location logical_loc, + int depth, + const char *desc, + diagnostic_thread_id_t thread_id) : m_loc (loc), - m_logical_loc (LOGICAL_LOCATION_KIND_FUNCTION, funcname), + m_logical_loc (logical_loc), m_depth (depth), m_desc (xstrdup (desc)), m_connected_to_next_event (false), m_thread_id (thread_id) diff --git a/gcc/selftest-diagnostic-path.h b/gcc/selftest-diagnostic-path.h index e20986c..8829943 100644 --- a/gcc/selftest-diagnostic-path.h +++ b/gcc/selftest-diagnostic-path.h @@ -41,7 +41,9 @@ namespace selftest { class test_diagnostic_event : public diagnostic_event { public: - test_diagnostic_event (location_t loc, const char *funcname, int depth, + test_diagnostic_event (location_t loc, + logical_location logical_loc, + int depth, const char *desc, diagnostic_thread_id_t thread_id = 0); ~test_diagnostic_event (); @@ -52,12 +54,9 @@ class test_diagnostic_event : public diagnostic_event { pp_string (&pp, m_desc); } - const logical_location *get_logical_location () const final override + logical_location get_logical_location () const final override { - if (m_logical_loc.get_name ()) - return &m_logical_loc; - else - return nullptr; + return m_logical_loc; } meaning get_meaning () const final override { @@ -77,14 +76,9 @@ class test_diagnostic_event : public diagnostic_event m_connected_to_next_event = true; } - const char *get_function_name () const - { - return m_logical_loc.get_name (); - } - private: location_t m_loc; - test_logical_location m_logical_loc; + logical_location m_logical_loc; int m_depth; char *m_desc; // has been formatted; doesn't get i18n-ed bool m_connected_to_next_event; @@ -149,6 +143,10 @@ class test_diagnostic_path : public diagnostic_path diagnostic_thread_id_t thread_id = 0); private: + logical_location + logical_location_from_funcname (const char *funcname); + + test_logical_location_manager m_test_logical_loc_mgr; auto_delete_vec<test_diagnostic_thread> m_threads; auto_delete_vec<test_diagnostic_event> m_events; diff --git a/gcc/selftest-diagnostic.cc b/gcc/selftest-diagnostic.cc index 8cf47ab..1a10807 100644 --- a/gcc/selftest-diagnostic.cc +++ b/gcc/selftest-diagnostic.cc @@ -69,7 +69,7 @@ bool test_diagnostic_context::report (diagnostic_t kind, rich_location &richloc, const diagnostic_metadata *metadata, - int option, + diagnostic_option_id option, const char * fmt, ...) { va_list ap; diff --git a/gcc/selftest-diagnostic.h b/gcc/selftest-diagnostic.h index dccad97..c8f67a0 100644 --- a/gcc/selftest-diagnostic.h +++ b/gcc/selftest-diagnostic.h @@ -50,7 +50,7 @@ class test_diagnostic_context : public diagnostic_context report (diagnostic_t kind, rich_location &richloc, const diagnostic_metadata *metadata, - int option, + diagnostic_option_id option, const char * fmt, ...) ATTRIBUTE_GCC_DIAG(6,7); const char *test_show_locus (rich_location &richloc); diff --git a/gcc/selftest-logical-location.cc b/gcc/selftest-logical-location.cc index 6c1c757..5f33b48 100644 --- a/gcc/selftest-logical-location.cc +++ b/gcc/selftest-logical-location.cc @@ -21,49 +21,96 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #include "system.h" #include "coretypes.h" +#include "selftest.h" #include "selftest-logical-location.h" #if CHECKING_P namespace selftest { -/* class test_logical_location : public logical_location. */ +/* class test_logical_location_manager : public logical_location_manager. */ -test_logical_location::test_logical_location (enum logical_location_kind kind, - const char *name) -: m_kind (kind), - m_name (name) +test_logical_location_manager::~test_logical_location_manager () { + for (auto iter : m_name_to_item_map) + delete iter.second; } const char * -test_logical_location::get_short_name () const +test_logical_location_manager::get_short_name (key k) const { - return m_name; + auto item = item_from_key (k); + if (!item) + return nullptr; + return item->m_name; } const char * -test_logical_location::get_name_with_scope () const +test_logical_location_manager::get_name_with_scope (key k) const { - return m_name; + auto item = item_from_key (k); + return item->m_name; } const char * -test_logical_location::get_internal_name () const +test_logical_location_manager::get_internal_name (key k) const { - return m_name; + auto item = item_from_key (k); + return item->m_name; } enum logical_location_kind -test_logical_location::get_kind () const +test_logical_location_manager::get_kind (key k) const { - return m_kind; + auto item = item_from_key (k); + return item->m_kind; } label_text -test_logical_location::get_name_for_path_output () const +test_logical_location_manager::get_name_for_path_output (key k) const { - return label_text::borrow (m_name); + auto item = item_from_key (k); + return label_text::borrow (item->m_name); +} + +logical_location +test_logical_location_manager:: +logical_location_from_funcname (const char *funcname) +{ + const item *i = item_from_funcname (funcname); + return key::from_ptr (i); +} + +const test_logical_location_manager::item * +test_logical_location_manager::item_from_funcname (const char *funcname) +{ + if (!funcname) + return nullptr; + + if (item **slot = m_name_to_item_map.get (funcname)) + return *slot; + + item *i = new item (LOGICAL_LOCATION_KIND_FUNCTION, funcname); + m_name_to_item_map.put (funcname, i); + return i; +} + +/* Run all of the selftests within this file. */ + +void +selftest_logical_location_cc_tests () +{ + test_logical_location_manager mgr; + + ASSERT_FALSE (mgr.logical_location_from_funcname (nullptr)); + + logical_location loc_foo = mgr.logical_location_from_funcname ("foo"); + logical_location loc_bar = mgr.logical_location_from_funcname ("bar"); + + ASSERT_NE (loc_foo, loc_bar); + + ASSERT_STREQ (mgr.get_short_name (loc_foo), "foo"); + ASSERT_STREQ (mgr.get_short_name (loc_bar), "bar"); } } // namespace selftest diff --git a/gcc/selftest-logical-location.h b/gcc/selftest-logical-location.h index dea59d8..d9bf38f 100644 --- a/gcc/selftest-logical-location.h +++ b/gcc/selftest-logical-location.h @@ -30,24 +30,49 @@ along with GCC; see the file COPYING3. If not see namespace selftest { -/* Concrete subclass of logical_location for use in selftests. */ +/* Concrete subclass of logical_location_manager for use in selftests. */ -class test_logical_location : public logical_location +class test_logical_location_manager : public logical_location_manager { public: - test_logical_location (enum logical_location_kind kind, - const char *name); - virtual const char *get_short_name () const final override; - virtual const char *get_name_with_scope () const final override; - virtual const char *get_internal_name () const final override; - virtual enum logical_location_kind get_kind () const final override; - virtual label_text get_name_for_path_output () const final override; - - const char *get_name () const { return m_name; } - - private: - enum logical_location_kind m_kind; - const char *m_name; + ~test_logical_location_manager (); + + const char *get_short_name (key) const final override; + const char *get_name_with_scope (key) const final override; + const char *get_internal_name (key) const final override; + enum logical_location_kind get_kind (key) const final override; + label_text get_name_for_path_output (key) const final override; + key get_parent (key) const final override + { + return key (); + } + + logical_location + logical_location_from_funcname (const char *funcname); + +private: + struct item + { + item (enum logical_location_kind kind, + const char *name) + : m_kind (kind), + m_name (name) + { + } + + enum logical_location_kind m_kind; + const char *m_name; + }; + + const item * + item_from_funcname (const char *funcname); + + static const item *item_from_key (logical_location k) + { + return k.cast_to<const item *> (); + } + + hash_map<nofree_string_hash, item *> m_name_to_item_map; }; } // namespace selftest diff --git a/gcc/selftest-run-tests.cc b/gcc/selftest-run-tests.cc index f705501..3c12e8a 100644 --- a/gcc/selftest-run-tests.cc +++ b/gcc/selftest-run-tests.cc @@ -92,6 +92,7 @@ selftest::run_tests () digraph_cc_tests (); tristate_cc_tests (); ipa_modref_tree_cc_tests (); + selftest_logical_location_cc_tests (); /* Higher-level tests, or for components that other selftests don't rely on. */ diff --git a/gcc/selftest.h b/gcc/selftest.h index 52ee0f1..a0d2473 100644 --- a/gcc/selftest.h +++ b/gcc/selftest.h @@ -257,6 +257,7 @@ extern void read_rtl_function_cc_tests (); extern void rtl_tests_cc_tests (); extern void sbitmap_cc_tests (); extern void selftest_cc_tests (); +extern void selftest_logical_location_cc_tests (); extern void simple_diagnostic_path_cc_tests (); extern void simplify_rtx_cc_tests (); extern void spellcheck_cc_tests (); diff --git a/gcc/simple-diagnostic-path.cc b/gcc/simple-diagnostic-path.cc index 5af69b6..b756cd5 100644 --- a/gcc/simple-diagnostic-path.cc +++ b/gcc/simple-diagnostic-path.cc @@ -34,8 +34,11 @@ along with GCC; see the file COPYING3. If not see /* class simple_diagnostic_path : public diagnostic_path. */ -simple_diagnostic_path::simple_diagnostic_path (pretty_printer *event_pp) -: m_event_pp (event_pp), +simple_diagnostic_path:: +simple_diagnostic_path (const tree_logical_location_manager &logical_loc_mgr, + pretty_printer *event_pp) +: diagnostic_path (logical_loc_mgr), + m_event_pp (event_pp), m_localize_events (true) { add_thread ("main"); @@ -161,7 +164,8 @@ simple_diagnostic_event (location_t loc, int depth, const char *desc, diagnostic_thread_id_t thread_id) -: m_loc (loc), m_fndecl (fndecl), m_logical_loc (fndecl), +: m_loc (loc), m_fndecl (fndecl), + m_logical_loc (tree_logical_location_manager::key_from_tree (fndecl)), m_depth (depth), m_desc (xstrdup (desc)), m_connected_to_next_event (false), m_thread_id (thread_id) @@ -188,11 +192,12 @@ namespace selftest { static void test_intraprocedural_path (pretty_printer *event_pp) { + tree_logical_location_manager mgr; tree fntype_void_void = build_function_type_array (void_type_node, 0, NULL); tree fndecl_foo = build_fn_decl ("foo", fntype_void_void); - simple_diagnostic_path path (event_pp); + simple_diagnostic_path path (mgr, event_pp); path.add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "first %qs", "free"); path.add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "double %qs", "free"); diff --git a/gcc/simple-diagnostic-path.h b/gcc/simple-diagnostic-path.h index e9a97f2..d2b366e 100644 --- a/gcc/simple-diagnostic-path.h +++ b/gcc/simple-diagnostic-path.h @@ -40,12 +40,9 @@ class simple_diagnostic_event : public diagnostic_event location_t get_location () const final override { return m_loc; } int get_stack_depth () const final override { return m_depth; } void print_desc (pretty_printer &pp) const final override; - const logical_location *get_logical_location () const final override + logical_location get_logical_location () const final override { - if (m_fndecl) - return &m_logical_loc; - else - return nullptr; + return tree_logical_location_manager::key_from_tree (m_fndecl); } meaning get_meaning () const final override { @@ -70,7 +67,7 @@ class simple_diagnostic_event : public diagnostic_event private: location_t m_loc; tree m_fndecl; - tree_logical_location m_logical_loc; + logical_location m_logical_loc; int m_depth; char *m_desc; // has been i18n-ed and formatted bool m_connected_to_next_event; @@ -98,7 +95,8 @@ private: class simple_diagnostic_path : public diagnostic_path { public: - simple_diagnostic_path (pretty_printer *event_pp); + simple_diagnostic_path (const tree_logical_location_manager &logical_loc_mgr, + pretty_printer *event_pp); unsigned num_events () const final override { return m_events.length (); } const diagnostic_event & get_event (int idx) const final override; diff --git a/gcc/testsuite/g++.dg/sarif-output/logical-locations-1.C b/gcc/testsuite/g++.dg/sarif-output/logical-locations-1.C new file mode 100644 index 0000000..75ad3b4 --- /dev/null +++ b/gcc/testsuite/g++.dg/sarif-output/logical-locations-1.C @@ -0,0 +1,27 @@ +/* { dg-do compile } */ +/* { dg-options "-fdiagnostics-format=sarif-file" } */ + +/* Verify that we can capture the chain of parents of a + logical location (PR 116176). */ + +namespace ns { + class foo + { + void bar () + { + return 0; + } + }; +} + +/* We expect a failing compile due to the error, but the use of + -fdiagnostics-format=sarif-file means there should be no output to stderr. + DejaGnu injects this message; ignore it: + { dg-prune-output "exit status is 1" } */ + +/* Verify that some JSON was written to a file with the expected name: + { dg-final { verify-sarif-file } } */ + +/* Use a Python script to verify various properties about the generated + .sarif file: + { dg-final { run-sarif-pytest logical-locations-1.C "logical-locations-1.py" } } */ diff --git a/gcc/testsuite/g++.dg/sarif-output/logical-locations-1.py b/gcc/testsuite/g++.dg/sarif-output/logical-locations-1.py new file mode 100644 index 0000000..954f6df --- /dev/null +++ b/gcc/testsuite/g++.dg/sarif-output/logical-locations-1.py @@ -0,0 +1,79 @@ +from sarif import * + +import pytest + +@pytest.fixture(scope='function', autouse=True) +def sarif(): + return sarif_from_env() + +expected_filename = 'logical-locations-1.C' + +def test_result(sarif): + runs = sarif['runs'] + run = runs[0] + results = run['results'] + + # The textual form of the diagnostic would look like this: + # . PATH/logical-locations-1.C: In member function ‘void ns::foo::bar()’: + # . PATH/logical-locations-1.C:12:14: error: return-statement with a value, in function returning ‘void’ [-fpermissive] + # . 12 | return 0; + # . | ^ + assert len(results) == 1 + + result = results[0] + assert result['ruleId'] == '-fpermissive' + assert result['level'] == 'error' + assert result['message']['text'] \ + == "return-statement with a value, in function returning 'void'" + + locations = result['locations'] + assert len(locations) == 1 + + location = locations[0] + assert get_location_artifact_uri(location).endswith(expected_filename) + assert get_location_snippet_text(location) == ' return 0;\n' + +def test_logical_locations(sarif): + runs = sarif['runs'] + run = runs[0] + + # We expect 3 logical locations within the run: + assert len(run['logicalLocations']) == 3 + + assert run['logicalLocations'][0] \ + == {"name": "ns", + "fullyQualifiedName": "ns", + "kind": "namespace", + "index": 0} + assert run['logicalLocations'][1] \ + == {"name": "foo", + # Ideally we'd also have: + # "fullyQualifiedName": "ns::foo", + "kind": "type", + "parentIndex": 0, + "index": 1} + assert run['logicalLocations'][2] \ + == {"name": "bar", + "fullyQualifiedName": "ns::foo::bar", + "decoratedName": "_ZN2ns3foo3barEv", + "kind": "function", + "parentIndex": 1, + "index": 2} + + results = run['results'] + + assert len(results) == 1 + + result = results[0] + + locations = result['locations'] + assert len(locations) == 1 + + location = locations[0] + + # We expect one logical location within the result, referencing + # one in the run + assert len(location['logicalLocations']) == 1 + assert location['logicalLocations'][0] \ + == {'fullyQualifiedName': 'ns::foo::bar', + 'index': 2} diff --git a/gcc/testsuite/g++.dg/sarif-output/logical-locations-2.C b/gcc/testsuite/g++.dg/sarif-output/logical-locations-2.C new file mode 100644 index 0000000..67aa4c9 --- /dev/null +++ b/gcc/testsuite/g++.dg/sarif-output/logical-locations-2.C @@ -0,0 +1,69 @@ +/* { dg-do compile } */ +/* { dg-options "-fdiagnostics-format=sarif-file" } */ + +/* Verify that we correctly consolidate logical locations + involving repeated diagnostics within a nested hierarchy + (PR 116176). */ + +namespace ns_outer { + namespace ns_inner_1 { + class klass_1 + { + void member_fn_1 () + { + return 0; + } + void member_fn_2 () + { + return 0; + } + }; + class klass_2 + { + void member_fn_1 () + { + return 0; + } + void member_fn_2 () + { + return 0; + } + }; + } // ns_inner_1 + namespace ns_inner_2 { + class klass_1 + { + void member_fn_1 () + { + return 0; + } + void member_fn_2 () + { + return 0; + } + }; + class klass_2 + { + void member_fn_1 () + { + return 0; + } + void member_fn_2 () + { + return 0; + } + }; + } // ns_inner_2 +} // ns_outer + +/* We expect a failing compile due to the error, but the use of + -fdiagnostics-format=sarif-file means there should be no output to stderr. + DejaGnu injects this message; ignore it: + { dg-prune-output "exit status is 1" } */ + +/* Verify that some JSON was written to a file with the expected name: + { dg-final { verify-sarif-file } } */ + +/* Use a Python script to verify various properties about the generated + .sarif file: + { dg-final { run-sarif-pytest logical-locations-2.C "logical-locations-2.py" } } */ diff --git a/gcc/testsuite/g++.dg/sarif-output/logical-locations-2.py b/gcc/testsuite/g++.dg/sarif-output/logical-locations-2.py new file mode 100644 index 0000000..e34531b --- /dev/null +++ b/gcc/testsuite/g++.dg/sarif-output/logical-locations-2.py @@ -0,0 +1,90 @@ +from sarif import * +from pprint import pprint + +import pytest + +@pytest.fixture(scope='function', autouse=True) +def sarif(): + return sarif_from_env() + +expected_filename = 'logical-locations-2.C' + +def test_logical_locations(sarif): + runs = sarif['runs'] + run = runs[0] + + # We expect 15 logical locations within the run expressing + # a logical hierarchy, and 8 results with one logical locations + # in each, referencing back into the run. + + # Generate the "gold" output we expect. + expected_in_run = [] + expected_in_results = [] + + outer_ns_index_in_run = len(expected_in_run) + expected_in_run += [{"name": "ns_outer", + "fullyQualifiedName": "ns_outer", + "kind": "namespace", + "index": outer_ns_index_in_run}] + + for inner_ns in ['ns_inner_1', 'ns_inner_2']: + inner_ns_idx_in_run = len(expected_in_run) + expected_in_run += [{"name": inner_ns, + "fullyQualifiedName": f"ns_outer::{inner_ns}", + "kind": "namespace", + "index": inner_ns_idx_in_run, + "parentIndex": 0}] + for klass in ['klass_1', 'klass_2']: + klass_idx_in_run = len(expected_in_run) + expected_in_run += [{"name": klass, + "kind": "type", + "index": klass_idx_in_run, + "parentIndex": inner_ns_idx_in_run}] + for member_fn in ['member_fn_1', 'member_fn_2']: + fqn = f'ns_outer::{inner_ns}::{klass}::{member_fn}' + member_fn_idx_in_run = len(expected_in_run) + expected_in_run += [{"name": member_fn, + "kind": "function", + "fullyQualifiedName": f"ns_outer::{inner_ns}::{klass}::{member_fn}", + "decoratedName": f"_ZN8ns_outer10{inner_ns}7{klass}11{member_fn}Ev", + "index": member_fn_idx_in_run, + "parentIndex": klass_idx_in_run}] + expected_in_results += [{'fullyQualifiedName': fqn, + 'index': member_fn_idx_in_run}] + + pprint(expected_in_run) + pprint(expected_in_results) + assert len(expected_in_run) == 15 + assert len(expected_in_results) == 8 + + # We expect 15 logical locations within the run: + assert len(run['logicalLocations']) == len(expected_in_run) + for actual, expected in zip(run['logicalLocations'], expected_in_run): + assert actual == expected + + # We expect 8 results with one logical location in each + results = run['results'] + assert len(results) == len(expected_in_results) + + index = 0 + for inner_ns in ['ns_inner_1', 'ns_inner_2']: + for klass in ['klass_1', 'klass_2']: + for member_fn in ['member_fn_1', 'member_fn_2']: + result = results[index] + assert result['ruleId'] == '-fpermissive' + assert result['level'] == 'error' + assert result['message']['text'] \ + == "return-statement with a value, in function returning 'void'" + + locations = result['locations'] + assert len(locations) == 1 + + location = locations[0] + + # We expect one logical location within the result, referencing + # one in the run + assert len(location['logicalLocations']) == 1 + assert location['logicalLocations'][0] \ + == expected_in_results[index] + + index += 1 diff --git a/gcc/testsuite/g++.dg/sarif-output/logical-locations-3.C b/gcc/testsuite/g++.dg/sarif-output/logical-locations-3.C new file mode 100644 index 0000000..59ed84a --- /dev/null +++ b/gcc/testsuite/g++.dg/sarif-output/logical-locations-3.C @@ -0,0 +1,85 @@ +/* { dg-do compile } */ +/* { dg-options "-fdiagnostics-format=sarif-file" } */ + +/* Verify that we handle deeply nested logical locations + (PR 116176). */ + +#define NS_OPEN(SUFFIX) namespace ns_##SUFFIX { +#define NS_CLOSE } + +#define NS_OPEN_10(SUFFIX) \ + NS_OPEN(SUFFIX##0) \ + NS_OPEN(SUFFIX##1) \ + NS_OPEN(SUFFIX##2) \ + NS_OPEN(SUFFIX##3) \ + NS_OPEN(SUFFIX##4) \ + NS_OPEN(SUFFIX##5) \ + NS_OPEN(SUFFIX##6) \ + NS_OPEN(SUFFIX##7) \ + NS_OPEN(SUFFIX##8) \ + NS_OPEN(SUFFIX##9) + +#define NS_CLOSE_10 \ + NS_CLOSE \ + NS_CLOSE \ + NS_CLOSE \ + NS_CLOSE \ + NS_CLOSE \ + NS_CLOSE \ + NS_CLOSE \ + NS_CLOSE \ + NS_CLOSE \ + NS_CLOSE + +#define NS_OPEN_100(SUFFIX) \ + NS_OPEN_10(SUFFIX##0) \ + NS_OPEN_10(SUFFIX##1) \ + NS_OPEN_10(SUFFIX##2) \ + NS_OPEN_10(SUFFIX##3) \ + NS_OPEN_10(SUFFIX##4) \ + NS_OPEN_10(SUFFIX##5) \ + NS_OPEN_10(SUFFIX##6) \ + NS_OPEN_10(SUFFIX##7) \ + NS_OPEN_10(SUFFIX##8) \ + NS_OPEN_10(SUFFIX##9) + +#define NS_CLOSE_100 \ + NS_CLOSE_10 \ + NS_CLOSE_10 \ + NS_CLOSE_10 \ + NS_CLOSE_10 \ + NS_CLOSE_10 \ + NS_CLOSE_10 \ + NS_CLOSE_10 \ + NS_CLOSE_10 \ + NS_CLOSE_10 \ + NS_CLOSE_10 + +#define NS_OPEN_200 \ + NS_OPEN_100(a) \ + NS_OPEN_100(b) + +#define NS_CLOSE_200 \ + NS_CLOSE_100 \ + NS_CLOSE_100 + +NS_OPEN_200 + +void return_from_void () +{ + return 0; +} + +NS_CLOSE_200 + +/* We expect a failing compile due to the error, but the use of + -fdiagnostics-format=sarif-file means there should be no output to stderr. + DejaGnu injects this message; ignore it: + { dg-prune-output "exit status is 1" } */ + +/* Verify that some JSON was written to a file with the expected name: + { dg-final { verify-sarif-file } } */ + +/* Use a Python script to verify various properties about the generated + .sarif file: + { dg-final { run-sarif-pytest logical-locations-3.C "logical-locations-3.py" } } */ diff --git a/gcc/testsuite/g++.dg/sarif-output/logical-locations-3.py b/gcc/testsuite/g++.dg/sarif-output/logical-locations-3.py new file mode 100644 index 0000000..199299c --- /dev/null +++ b/gcc/testsuite/g++.dg/sarif-output/logical-locations-3.py @@ -0,0 +1,61 @@ +from sarif import * +from pprint import pprint + +import pytest + +@pytest.fixture(scope='function', autouse=True) +def sarif(): + return sarif_from_env() + +expected_filename = 'logical-locations-3.C' + +def test_logical_locations(sarif): + runs = sarif['runs'] + run = runs[0] + + # We expect a single error with this very long logical location: + # ../../src/gcc/testsuite/g++.dg/sarif-output/logical-locations-3.C: In function ‘void ns_a00::ns_a01::ns_a02::ns_a03::ns_a04::ns_a05::ns_a06::ns_a07::ns_a08::ns_a09::ns_a10::ns_a11::ns_a12::ns_a13::ns_a14::ns_a15::ns_a16::ns_a17::ns_a18::ns_a19::ns_a20::ns_a21::ns_a22::ns_a23::ns_a24::ns_a25::ns_a26::ns_a27::ns_a28::ns_a29::ns_a30::ns_a31::ns_a32::ns_a33::ns_a34::ns_a35::ns_a36::ns_a37::ns_a38::ns_a39::ns_a40::ns_a41::ns_a42::ns_a43::ns_a44::ns_a45::ns_a46::ns_a47::ns_a48::ns_a49::ns_a50::ns_a51::ns_a52::ns_a53::ns_a54::ns_a55::ns_a56::ns_a57::ns_a58::ns_a59::ns_a60::ns_a61::ns_a62::ns_a63::ns_a64::ns_a65::ns_a66::ns_a67::ns_a68::ns_a69::ns_a70::ns_a71::ns_a72::ns_a73::ns_a74::ns_a75::ns_a76::ns_a77::ns_a78::ns_a79::ns_a80::ns_a81::ns_a82::ns_a83::ns_a84::ns_a85::ns_a86::ns_a87::ns_a88::ns_a89::ns_a90::ns_a91::ns_a92::ns_a93::ns_a94::ns_a95::ns_a96::ns_a97::ns_a98::ns_a99::ns_b00::ns_b01::ns_b02::ns_b03::ns_b04::ns_b05::ns_b06::ns_b07::ns_b08::ns_b09::ns_b10::ns_b11::ns_b12::ns_b13::ns_b14::ns_b15::ns_b16::ns_b17::ns_b18::ns_b19::ns_b20::ns_b21::ns_b22::ns_b23::ns_b24::ns_b25::ns_b26::ns_b27::ns_b28::ns_b29::ns_b30::ns_b31::ns_b32::ns_b33::ns_b34::ns_b35::ns_b36::ns_b37::ns_b38::ns_b39::ns_b40::ns_b41::ns_b42::ns_b43::ns_b44::ns_b45::ns_b46::ns_b47::ns_b48::ns_b49::ns_b50::ns_b51::ns_b52::ns_b53::ns_b54::ns_b55::ns_b56::ns_b57::ns_b58::ns_b59::ns_b60::ns_b61::ns_b62::ns_b63::ns_b64::ns_b65::ns_b66::ns_b67::ns_b68::ns_b69::ns_b70::ns_b71::ns_b72::ns_b73::ns_b74::ns_b75::ns_b76::ns_b77::ns_b78::ns_b79::ns_b80::ns_b81::ns_b82::ns_b83::ns_b84::ns_b85::ns_b86::ns_b87::ns_b88::ns_b89::ns_b90::ns_b91::ns_b92::ns_b93::ns_b94::ns_b95::ns_b96::ns_b97::ns_b98::ns_b99::return_from_void()’: + # ../../src/gcc/testsuite/g++.dg/sarif-output/logical-locations-3.C:70:10: error: return-statement with a value, in function returning ‘void’ [-fpermissive] + # 70 | return 0; + # | ^ + + # We expect 201 logical locations within the run expressing + # the logical hierarchy: the 200 nested namespaces, and then + # the function within the innermost namespace. + assert len(run['logicalLocations']) == 201 + + outermost = run['logicalLocations'][0] + assert outermost == {'fullyQualifiedName': 'ns_a00', + 'index': 0, + 'kind': 'namespace', + 'name': 'ns_a00'} + + for i in range(1, 200): + ns_i = run['logicalLocations'][i] + assert ns_i['index'] == i + assert ns_i['kind'] == 'namespace' + assert ns_i['parentIndex'] == i - 1 + expected_name = 'ns_' + 'ab'[i // 100] + '%02i' % (i % 100) + assert ns_i['name'] == expected_name + assert ns_i['fullyQualifiedName'] \ + == run['logicalLocations'][i - 1]['fullyQualifiedName'] + "::" + expected_name + + innermost = run['logicalLocations'][200] + assert innermost['index'] == 200 + assert innermost['kind'] == 'function' + assert innermost['name'] == 'return_from_void' + assert innermost['parentIndex'] == 199 + assert innermost['fullyQualifiedName'] \ + == run['logicalLocations'][199]['fullyQualifiedName'] + '::return_from_void' + + # We expect 1 error in the run, referring to the innermost + # logical location by index within the run's logical locations + results = run['results'] + assert len(results) == 1 + result = results[0] + assert len(result['locations']) == 1 + assert len(result['locations'][0]['logicalLocations']) == 1 + assert result['locations'][0]['logicalLocations'][0]['index'] \ + == innermost['index'] + assert result['locations'][0]['logicalLocations'][0]['fullyQualifiedName'] \ + == innermost['fullyQualifiedName'] diff --git a/gcc/testsuite/g++.dg/sarif-output/sarif-output.exp b/gcc/testsuite/g++.dg/sarif-output/sarif-output.exp new file mode 100644 index 0000000..20b2845 --- /dev/null +++ b/gcc/testsuite/g++.dg/sarif-output/sarif-output.exp @@ -0,0 +1,31 @@ +# Copyright (C) 2012-2025 Free Software Foundation, Inc. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GCC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +# GCC testsuite that uses the `dg.exp' driver. + +# Load support procs. +load_lib g++-dg.exp + +# Initialize `dg'. +dg-init + +# Main loop. +dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C]] "" "" + +# All done. +dg-finish diff --git a/gcc/testsuite/gcc.dg/ipa/pr119852.c b/gcc/testsuite/gcc.dg/ipa/pr119852.c new file mode 100644 index 0000000..eab8d212 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/pr119852.c @@ -0,0 +1,50 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fdump-ipa-clones" } */ + +typedef struct rtx_def *rtx; +enum rtx_code { + LAST_AND_UNUSED_RTX_CODE}; +extern const char * const rtx_format[((int) LAST_AND_UNUSED_RTX_CODE)]; +struct rtx_def { + enum rtx_code code; +}; +typedef int (*rtx_function) (rtx *, void *); +extern int for_each_rtx (rtx *, rtx_function, void *); +int +replace_label (rtx *x, void *data) +{ + rtx l = *x; + if (l == (rtx) 0) + { + { + rtx new_c, new_l; + for_each_rtx (&new_c, replace_label, data); + } + } +} +static int +for_each_rtx_1 (rtx exp, int n, rtx_function f, void *data) +{ + int result, i, j; + const char *format = (rtx_format[(int) (((enum rtx_code) (exp)->code))]); + rtx *x; + for (; format[n] != '\0'; n++) + { + switch (format[n]) + { + case 'e': + result = (*f) (x, data); + { + result = for_each_rtx_1 (*x, i, f, data); + } + } + } +} +int +for_each_rtx (rtx *x, rtx_function f, void *data) +{ + int i; + return for_each_rtx_1 (*x, i, f, data); +} + +/* { dg-final { scan-ipa-dump-not "(null)" "ipa-clones" } } */ diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_paths.cc b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_paths.cc index 954538f..a7963fa 100644 --- a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_paths.cc +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_paths.cc @@ -147,7 +147,9 @@ example_1 () { auto_diagnostic_group d; gcc_rich_location richloc (gimple_location (call_to_PyList_Append)); - simple_diagnostic_path path (global_dc->get_reference_printer ()); + tree_logical_location_manager logical_loc_mgr; + simple_diagnostic_path path (logical_loc_mgr, + global_dc->get_reference_printer ()); diagnostic_event_id_t alloc_event_id = path.add_event (gimple_location (call_to_PyList_New), example_a_fun->decl, 0, @@ -214,7 +216,8 @@ class test_diagnostic_path : public simple_diagnostic_path { public: test_diagnostic_path (pretty_printer *event_pp) - : simple_diagnostic_path (event_pp) + : simple_diagnostic_path (m_logical_loc_mgr, + event_pp) { } diagnostic_event_id_t @@ -262,6 +265,9 @@ class test_diagnostic_path : public simple_diagnostic_path add_event (call_evloc.m_loc, call_evloc.m_fun->decl, caller_stack_depth, "calling %qs", callee); } + +private: + tree_logical_location_manager m_logical_loc_mgr; }; static void diff --git a/gcc/testsuite/gcc.dg/pr120074.c b/gcc/testsuite/gcc.dg/pr120074.c new file mode 100644 index 0000000..3f31516 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr120074.c @@ -0,0 +1,20 @@ +/* PR tree-optimization/120074 */ +/* { dg-do compile } */ +/* { dg-options "-O1 -fno-tree-copy-prop -fno-tree-forwprop -fno-tree-ccp" } */ + +int foo (int); +short a; +int b; + +int +bar (int d, int e) +{ + return d < 0 || d > __INT_MAX__ >> e; +} + +int +main () +{ + int f = bar ((b ^ a) & 3, __SIZEOF_INT__ * __CHAR_BIT__ - 2); + foo (f); +} diff --git a/gcc/testsuite/gcc.dg/vect/bb-slp-pr115777.c b/gcc/testsuite/gcc.dg/vect/bb-slp-pr115777.c new file mode 100644 index 0000000..bba0dc7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/vect/bb-slp-pr115777.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ + +typedef unsigned int T; + +#define SWAP(A, B) do { T tmp = A; A = B; B = tmp; } while (0) + +void +insertion_sort(T *v, int n) +{ + for (int i = 1; i < n; ++i) + for (int k = i; k > 0 && v[k-1] > v[k]; --k) + SWAP(v[k-1], v[k]); +} + +/* { dg-final { scan-tree-dump "using element-wise load" "slp1" { target { { x86_64-*-* i?86-*-* } && { ! ia32 } } } } } */ diff --git a/gcc/testsuite/gcc.target/riscv/pr114512.c b/gcc/testsuite/gcc.target/riscv/pr114512.c new file mode 100644 index 0000000..205071c --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/pr114512.c @@ -0,0 +1,109 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcb -mabi=lp64d" { target { rv64 } } } */ +/* { dg-options "-march=rv32gcb -mabi=ilp32" { target { rv32 } } } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-Og" "-Os" "-Oz" } } */ + +/* We need to adjust the constant so this works for rv32 and rv64. */ +#if __riscv_xlen == 32 +#define ONE 1U +#define MASK 0x1f +typedef unsigned int utype; +#else +#define ONE 1ULL +#define MASK 0x3f +typedef unsigned long utype; +#endif + + +_Bool my_isxdigit_1(unsigned char ch) { + utype mask1 = 0x03FF007E; + if (!((mask1 >> (ch & MASK)) & 1)) + return 0; + + return 1; +} + +_Bool my_isxdigit_1a(unsigned char ch) { + utype mask2 = 0x58; + if (!((mask2 >> (ch >> 4)) & 1)) + return 0; + + return 1; +} + +_Bool my_isxdigit_2(unsigned char ch) { + utype mask1 = 0x03FF007E; + if (!(mask1 & (ONE << (ch & MASK)))) + return 0; + + return 1; +} + +_Bool my_isxdigit_2a(unsigned char ch) { + utype mask2 = 0x58; + if (!(mask2 & (ONE << (ch >> 4)))) + return 0; + + return 1; +} + +_Bool my_isxdigit_3(unsigned char ch) { + utype mask1 = 0x7E00FFC0; + if (!((mask1 << (MASK - (ch & MASK))) >> MASK)) + return 0; + + return 1; +} + +_Bool my_isxdigit_3a(unsigned char ch) { + utype mask2 = 0x7E00FFC0; + if (!((mask2 << (MASK - ((ch >> 4) & MASK))) >> MASK)) + return 0; + + return 1; +} + +_Bool my_isxdigit_1_parm(unsigned char ch, utype mask1) { + if (!((mask1 >> (ch & MASK)) & 1)) + return 0; + + return 1; +} + +_Bool my_isxdigit_1a_parm(unsigned char ch, utype mask2) { + if (!((mask2 >> (ch >> 4)) & 1)) + return 0; + + return 1; +} + +_Bool my_isxdigit_2_parm(unsigned char ch, utype mask1) { + if (!(mask1 & (ONE << (ch & MASK)))) + return 0; + + return 1; +} + +_Bool my_isxdigit_2a_parm(unsigned char ch, utype mask2) { + if (!(mask2 & (ONE << (ch >> 4)))) + return 0; + + return 1; +} + +_Bool my_isxdigit_3_parm(unsigned char ch, utype mask1) { + if (!((mask1 << (MASK - (ch & MASK))) >> MASK)) + return 0; + + return 1; +} + +_Bool my_isxdigit_3a_parm(unsigned char ch, utype mask2) { + if (!((mask2 << (MASK - ((ch >> 4) & MASK))) >> MASK)) + return 0; + + return 1; +} + +/* Each test should generate a single bext. */ +/* { dg-final { scan-assembler-times "bext\t" 12 } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_binary.h b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_binary.h new file mode 100644 index 0000000..66654eb --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_binary.h @@ -0,0 +1,17 @@ +#ifndef HAVE_DEFINED_VX_VF_BINARY_H +#define HAVE_DEFINED_VX_VF_BINARY_H + +#include <stdint.h> + +#define DEF_VX_BINARY(T, OP) \ +void \ +test_vx_binary (T * restrict out, T * restrict in, T x, unsigned n) \ +{ \ + for (unsigned i = 0; i < n; i++) \ + out[i] = in[i] OP x; \ +} +#define DEF_VX_BINARY_WRAP(T, OP) DEF_VX_BINARY(T, OP) +#define RUN_VX_BINARY(out, in, x, n) test_vx_binary(out, in, x, n) +#define RUN_VX_BINARY_WRAP(out, in, x, n) RUN_VX_BINARY(out, in, x, n) + +#endif diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_binary_data.h b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_binary_data.h new file mode 100644 index 0000000..11a32cb --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_binary_data.h @@ -0,0 +1,401 @@ +#ifndef HAVE_DEFINED_VX_BINARY_DATA_H +#define HAVE_DEFINED_VX_BINARY_DATA_H + +#define N 16 + +#define TEST_BINARY_DATA(T, NAME) test_##T##_##NAME##_data +#define TEST_BINARY_DATA_WRAP(T, NAME) TEST_BINARY_DATA(T, NAME) + +int8_t TEST_BINARY_DATA(int8_t, vadd)[][3][N] = +{ + { + { 1 }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + -1, -1, -1, -1, + -2, -2, -2, -2, + }, + { + 1, 1, 1, 1, + 2, 2, 2, 2, + 0, 0, 0, 0, + -1, -1, -1, -1, + }, + }, + { + { 127 }, + { + 0, 0, 0, 0, + -1, -1, -1, -1, + -128, -128, -128, -128, + -2, -2, -2, -2, + }, + { + 127, 127, 127, 127, + 126, 126, 126, 126, + -1, -1, -1, -1, + 125, 125, 125, 125, + }, + }, + { + { -128 }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + 127, 127, 127, 127, + 2, 2, 2, 2, + }, + { + -128, -128, -128, -128, + -127, -127, -127, -127, + -1, -1, -1, -1, + -126, -126, -126, -126, + }, + }, +}; + +int16_t TEST_BINARY_DATA(int16_t, vadd)[][3][N] = +{ + { + { 1 }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + -1, -1, -1, -1, + -2, -2, -2, -2, + }, + { + 1, 1, 1, 1, + 2, 2, 2, 2, + 0, 0, 0, 0, + -1, -1, -1, -1, + }, + }, + { + { 32767 }, + { + 0, 0, 0, 0, + -1, -1, -1, -1, + -32768, -32768, -32768, -32768, + -2, -2, -2, -2, + }, + { + 32767, 32767, 32767, 32767, + 32766, 32766, 32766, 32766, + -1, -1, -1, -1, + 32765, 32765, 32765, 32765, + }, + }, + { + { -32768 }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + 32767, 32767, 32767, 32767, + 2, 2, 2, 2, + }, + { + -32768, -32768, -32768, -32768, + -32767, -32767, -32767, -32767, + -1, -1, -1, -1, + -32766, -32766, -32766, -32766, + }, + }, +}; + +int32_t TEST_BINARY_DATA(int32_t, vadd)[][3][N] = +{ + { + { 1 }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + -1, -1, -1, -1, + -2, -2, -2, -2, + }, + { + 1, 1, 1, 1, + 2, 2, 2, 2, + 0, 0, 0, 0, + -1, -1, -1, -1, + }, + }, + { + { 2147483647 }, + { + 0, 0, 0, 0, + -1, -1, -1, -1, + -2147483648, -2147483648, -2147483648, -2147483648, + -2, -2, -2, -2, + }, + { + 2147483647, 2147483647, 2147483647, 2147483647, + 2147483646, 2147483646, 2147483646, 2147483646, + -1, -1, -1, -1, + 2147483645, 2147483645, 2147483645, 2147483645, + }, + }, + { + { -2147483648 }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + 2147483647, 2147483647, 2147483647, 2147483647, + 2, 2, 2, 2, + }, + { + -2147483648, -2147483648, -2147483648, -2147483648, + -2147483647, -2147483647, -2147483647, -2147483647, + -1, -1, -1, -1, + -2147483646, -2147483646, -2147483646, -2147483646, + }, + }, +}; + +int64_t TEST_BINARY_DATA(int64_t, vadd)[][3][N] = +{ + { + { 1 }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + -1, -1, -1, -1, + -2, -2, -2, -2, + }, + { + 1, 1, 1, 1, + 2, 2, 2, 2, + 0, 0, 0, 0, + -1, -1, -1, -1, + }, + }, + { + { 9223372036854775807ll }, + { + 0, 0, 0, 0, + -1, -1, -1, -1, + -9223372036854775808ull, -9223372036854775808ull, -9223372036854775808ull, -9223372036854775808ull, + -2, -2, -2, -2, + }, + { + 9223372036854775807ll, 9223372036854775807ll, 9223372036854775807ll, 9223372036854775807ll, + 9223372036854775806ll, 9223372036854775806ll, 9223372036854775806ll, 9223372036854775806ll, + -1, -1, -1, -1, + 9223372036854775805ll, 9223372036854775805ll, 9223372036854775805ll, 9223372036854775805ll, + }, + }, + { + { -9223372036854775808ull }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + 9223372036854775807ll, 9223372036854775807ll, 9223372036854775807ll, 9223372036854775807ll, + 2, 2, 2, 2, + }, + { + -9223372036854775808ull, -9223372036854775808ull, -9223372036854775808ull, -9223372036854775808ull, + -9223372036854775807ll, -9223372036854775807ll, -9223372036854775807ll, -9223372036854775807ll, + -1, -1, -1, -1, + -9223372036854775806ll, -9223372036854775806ll, -9223372036854775806ll, -9223372036854775806ll, + }, + }, +}; + +uint8_t TEST_BINARY_DATA(uint8_t, vadd)[][3][N] = +{ + { + { 1 }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + 11, 11, 11, 11, + 9, 9, 9, 9, + }, + { + 1, 1, 1, 1, + 2, 2, 2, 2, + 12, 12, 12, 12, + 10, 10, 10, 10, + }, + }, + { + { 127 }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + 127, 127, 127, 127, + 128, 128, 128, 128, + }, + { + 127, 127, 127, 127, + 128, 128, 128, 128, + 254, 254, 254, 254, + 255, 255, 255, 255, + }, + }, + { + { 253 }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + 2, 2, 2, 2, + 255, 255, 255, 255, + }, + { + 253, 253, 253, 253, + 254, 254, 254, 254, + 255, 255, 255, 255, + 252, 252, 252, 252, + }, + }, +}; + +uint16_t TEST_BINARY_DATA(uint16_t, vadd)[][3][N] = +{ + { + { 1 }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + 11, 11, 11, 11, + 9, 9, 9, 9, + }, + { + 1, 1, 1, 1, + 2, 2, 2, 2, + 12, 12, 12, 12, + 10, 10, 10, 10, + }, + }, + { + { 32767 }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + 32767, 32767, 32767, 32767, + 32768, 32768, 32768, 32768, + }, + { + 32767, 32767, 32767, 32767, + 32768, 32768, 32768, 32768, + 65534, 65534, 65534, 65534, + 65535, 65535, 65535, 65535, + }, + }, + { + { 65533 }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + 2, 2, 2, 2, + 65535, 65535, 65535, 65535, + }, + { + 65533, 65533, 65533, 65533, + 65534, 65534, 65534, 65534, + 65535, 65535, 65535, 65535, + 65532, 65532, 65532, 65532, + }, + }, +}; + +uint32_t TEST_BINARY_DATA(uint32_t, vadd)[][3][N] = +{ + { + { 1 }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + 11, 11, 11, 11, + 9, 9, 9, 9, + }, + { + 1, 1, 1, 1, + 2, 2, 2, 2, + 12, 12, 12, 12, + 10, 10, 10, 10, + }, + }, + { + { 2147483647 }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + 2147483647, 2147483647, 2147483647, 2147483647, + 2147483648, 2147483648, 2147483648, 2147483648, + }, + { + 2147483647, 2147483647, 2147483647, 2147483647, + 2147483648, 2147483648, 2147483648, 2147483648, + 4294967294, 4294967294, 4294967294, 4294967294, + 4294967295, 4294967295, 4294967295, 4294967295, + }, + }, + { + { 4294967293 }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + 2, 2, 2, 2, + 4294967295, 4294967295, 4294967295, 4294967295, + }, + { + 4294967293, 4294967293, 4294967293, 4294967293, + 4294967294, 4294967294, 4294967294, 4294967294, + 4294967295, 4294967295, 4294967295, 4294967295, + 4294967292, 4294967292, 4294967292, 4294967292, + }, + }, +}; + +uint64_t TEST_BINARY_DATA(uint64_t, vadd)[][3][N] = +{ + { + { 1 }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + 11, 11, 11, 11, + 9, 9, 9, 9, + }, + { + 1, 1, 1, 1, + 2, 2, 2, 2, + 12, 12, 12, 12, + 10, 10, 10, 10, + }, + }, + { + { 9223372036854775807ull }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + 9223372036854775807ull, 9223372036854775807ull, 9223372036854775807ull, 9223372036854775807ull, + 9223372036854775808ull, 9223372036854775808ull, 9223372036854775808ull, 9223372036854775808ull, + }, + { + 9223372036854775807ull, 9223372036854775807ull, 9223372036854775807ull, 9223372036854775807ull, + 9223372036854775808ull, 9223372036854775808ull, 9223372036854775808ull, 9223372036854775808ull, + 18446744073709551614ull, 18446744073709551614ull, 18446744073709551614ull, 18446744073709551614ull, + 18446744073709551615ull, 18446744073709551615ull, 18446744073709551615ull, 18446744073709551615ull, + }, + }, + { + { 18446744073709551613ull }, + { + 0, 0, 0, 0, + 1, 1, 1, 1, + 2, 2, 2, 2, + 18446744073709551615ull, 18446744073709551615ull, 18446744073709551615ull, 18446744073709551615ull, + }, + { + 18446744073709551613ull, 18446744073709551613ull, 18446744073709551613ull, 18446744073709551613ull, + 18446744073709551614ull, 18446744073709551614ull, 18446744073709551614ull, 18446744073709551614ull, + 18446744073709551615ull, 18446744073709551615ull, 18446744073709551615ull, 18446744073709551615ull, + 18446744073709551612ull, 18446744073709551612ull, 18446744073709551612ull, 18446744073709551612ull, + }, + }, +}; + +#endif diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_binary_run.h b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_binary_run.h new file mode 100644 index 0000000..bb35184 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_binary_run.h @@ -0,0 +1,26 @@ +#ifndef HAVE_DEFINED_VX_BINARY_RUN_H +#define HAVE_DEFINED_VX_BINARY_RUN_H + +int +main () +{ + unsigned i, k; + T out[N]; + + for (i = 0; i < sizeof (TEST_DATA) / sizeof (TEST_DATA[0]); i++) + { + T x = TEST_DATA[i][0][0]; + T *in = TEST_DATA[i][1]; + T *expect = TEST_DATA[i][2]; + + TEST_RUN (out, in, x, N); + + for (k = 0; k < N; k++) + if (out[k] != expect[k]) + __builtin_abort (); + } + + return 0; +} + +#endif diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-i16.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-i16.c new file mode 100644 index 0000000..488bc75 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-i16.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=0" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(int16_t, +) + +/* { dg-final { scan-assembler-times {vadd.vx} 1 } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-i32.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-i32.c new file mode 100644 index 0000000..aeba835 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-i32.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=0" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(int32_t, +) + +/* { dg-final { scan-assembler-times {vadd.vx} 1 } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-i64.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-i64.c new file mode 100644 index 0000000..ed0b937 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-i64.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=0" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(int64_t, +) + +/* { dg-final { scan-assembler-times {vadd.vx} 1 } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-i8.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-i8.c new file mode 100644 index 0000000..781d3c0 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-i8.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=0" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(int8_t, +) + +/* { dg-final { scan-assembler-times {vadd.vx} 1 } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-u16.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-u16.c new file mode 100644 index 0000000..c01fc23 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-u16.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=0" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(uint16_t, +) + +/* { dg-final { scan-assembler-times {vadd.vx} 1 } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-u32.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-u32.c new file mode 100644 index 0000000..63198492 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-u32.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=0" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(uint32_t, +) + +/* { dg-final { scan-assembler-times {vadd.vx} 1 } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-u64.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-u64.c new file mode 100644 index 0000000..36eec53 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-u64.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=0" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(uint64_t, +) + +/* { dg-final { scan-assembler-times {vadd.vx} 1 } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-u8.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-u8.c new file mode 100644 index 0000000..6bf4b61 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-1-u8.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=0" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(uint8_t, +) + +/* { dg-final { scan-assembler-times {vadd.vx} 1 } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-i16.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-i16.c new file mode 100644 index 0000000..eb19938 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-i16.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=1" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(int16_t, +) + +/* { dg-final { scan-assembler-not {vadd.vx} } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-i32.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-i32.c new file mode 100644 index 0000000..24182c5 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-i32.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=1" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(int32_t, +) + +/* { dg-final { scan-assembler-not {vadd.vx} } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-i64.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-i64.c new file mode 100644 index 0000000..b3d3d4b --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-i64.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=1" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(int64_t, +) + +/* { dg-final { scan-assembler-not {vadd.vx} } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-i8.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-i8.c new file mode 100644 index 0000000..fb35315 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-i8.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=1" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(int8_t, +) + +/* { dg-final { scan-assembler-not {vadd.vx} } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-u16.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-u16.c new file mode 100644 index 0000000..6ba2658 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-u16.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=1" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(uint16_t, +) + +/* { dg-final { scan-assembler-not {vadd.vx} } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-u32.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-u32.c new file mode 100644 index 0000000..b60412c --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-u32.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=1" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(uint32_t, +) + +/* { dg-final { scan-assembler-not {vadd.vx} } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-u64.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-u64.c new file mode 100644 index 0000000..a273294 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-u64.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=1" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(uint64_t, +) + +/* { dg-final { scan-assembler-not {vadd.vx} } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-u8.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-u8.c new file mode 100644 index 0000000..f3c41f9 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-2-u8.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=1" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(uint8_t, +) + +/* { dg-final { scan-assembler-not {vadd.vx} } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-i16.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-i16.c new file mode 100644 index 0000000..f3a2627 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-i16.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=15" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(int16_t, +) + +/* { dg-final { scan-assembler-not {vadd.vx} } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-i32.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-i32.c new file mode 100644 index 0000000..490854c --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-i32.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=15" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(int32_t, +) + +/* { dg-final { scan-assembler-not {vadd.vx} } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-i64.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-i64.c new file mode 100644 index 0000000..a7448df --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-i64.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=15" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(int64_t, +) + +/* { dg-final { scan-assembler-not {vadd.vx} } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-i8.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-i8.c new file mode 100644 index 0000000..72c7cd8 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-i8.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=15" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(int8_t, +) + +/* { dg-final { scan-assembler-not {vadd.vx} } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-u16.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-u16.c new file mode 100644 index 0000000..552b4ed --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-u16.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=15" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(uint16_t, +) + +/* { dg-final { scan-assembler-not {vadd.vx} } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-u32.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-u32.c new file mode 100644 index 0000000..e319672 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-u32.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=15" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(uint32_t, +) + +/* { dg-final { scan-assembler-not {vadd.vx} } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-u64.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-u64.c new file mode 100644 index 0000000..6e2a89e --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-u64.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=15" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(uint64_t, +) + +/* { dg-final { scan-assembler-not {vadd.vx} } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-u8.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-u8.c new file mode 100644 index 0000000..d3383e2 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-3-u8.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv64gcv -mabi=lp64d --param=gpr2vr-cost=15" } */ + +#include "vx_binary.h" + +DEF_VX_BINARY(uint8_t, +) + +/* { dg-final { scan-assembler-not {vadd.vx} } } */ diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-i16.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-i16.c new file mode 100644 index 0000000..fb5375d --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-i16.c @@ -0,0 +1,14 @@ +/* { dg-do run { target { riscv_v } } } */ +/* { dg-additional-options "-std=c99 --param=gpr2vr-cost=0" } */ + +#include "vx_binary.h" +#include "vx_binary_data.h" + +#define T int16_t + +DEF_VX_BINARY_WRAP(T, +) + +#define TEST_DATA TEST_BINARY_DATA_WRAP(T, vadd) +#define TEST_RUN(out, in, x, n) RUN_VX_BINARY_WRAP(out, in, x, n) + +#include "vx_binary_run.h" diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-i32.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-i32.c new file mode 100644 index 0000000..c2c79f5 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-i32.c @@ -0,0 +1,14 @@ +/* { dg-do run { target { riscv_v } } } */ +/* { dg-additional-options "-std=c99 --param=gpr2vr-cost=0" } */ + +#include "vx_binary.h" +#include "vx_binary_data.h" + +#define T int32_t + +DEF_VX_BINARY_WRAP(T, +) + +#define TEST_DATA TEST_BINARY_DATA_WRAP(T, vadd) +#define TEST_RUN(out, in, x, n) RUN_VX_BINARY_WRAP(out, in, x, n) + +#include "vx_binary_run.h" diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-i64.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-i64.c new file mode 100644 index 0000000..541ed21 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-i64.c @@ -0,0 +1,14 @@ +/* { dg-do run { target { riscv_v } } } */ +/* { dg-additional-options "-std=c99 --param=gpr2vr-cost=0" } */ + +#include "vx_binary.h" +#include "vx_binary_data.h" + +#define T int64_t + +DEF_VX_BINARY_WRAP(T, +) + +#define TEST_DATA TEST_BINARY_DATA_WRAP(T, vadd) +#define TEST_RUN(out, in, x, n) RUN_VX_BINARY_WRAP(out, in, x, n) + +#include "vx_binary_run.h" diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-i8.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-i8.c new file mode 100644 index 0000000..d507bbb --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-i8.c @@ -0,0 +1,14 @@ +/* { dg-do run { target { riscv_v } } } */ +/* { dg-additional-options "-std=c99 --param=gpr2vr-cost=0" } */ + +#include "vx_binary.h" +#include "vx_binary_data.h" + +#define T int8_t + +DEF_VX_BINARY_WRAP(T, +) + +#define TEST_DATA TEST_BINARY_DATA_WRAP(T, vadd) +#define TEST_RUN(out, in, x, n) RUN_VX_BINARY_WRAP(out, in, x, n) + +#include "vx_binary_run.h" diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-u16.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-u16.c new file mode 100644 index 0000000..52c0749 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-u16.c @@ -0,0 +1,14 @@ +/* { dg-do run { target { riscv_v } } } */ +/* { dg-additional-options "-std=c99 --param=gpr2vr-cost=0" } */ + +#include "vx_binary.h" +#include "vx_binary_data.h" + +#define T uint16_t + +DEF_VX_BINARY_WRAP(T, +) + +#define TEST_DATA TEST_BINARY_DATA_WRAP(T, vadd) +#define TEST_RUN(out, in, x, n) RUN_VX_BINARY_WRAP(out, in, x, n) + +#include "vx_binary_run.h" diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-u32.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-u32.c new file mode 100644 index 0000000..70dc347 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-u32.c @@ -0,0 +1,14 @@ +/* { dg-do run { target { riscv_v } } } */ +/* { dg-additional-options "-std=c99 --param=gpr2vr-cost=0" } */ + +#include "vx_binary.h" +#include "vx_binary_data.h" + +#define T uint32_t + +DEF_VX_BINARY_WRAP(T, +) + +#define TEST_DATA TEST_BINARY_DATA_WRAP(T, vadd) +#define TEST_RUN(out, in, x, n) RUN_VX_BINARY_WRAP(out, in, x, n) + +#include "vx_binary_run.h" diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-u64.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-u64.c new file mode 100644 index 0000000..6ce0060 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-u64.c @@ -0,0 +1,14 @@ +/* { dg-do run { target { riscv_v } } } */ +/* { dg-additional-options "-std=c99 --param=gpr2vr-cost=0" } */ + +#include "vx_binary.h" +#include "vx_binary_data.h" + +#define T uint64_t + +DEF_VX_BINARY_WRAP(T, +) + +#define TEST_DATA TEST_BINARY_DATA_WRAP(T, vadd) +#define TEST_RUN(out, in, x, n) RUN_VX_BINARY_WRAP(out, in, x, n) + +#include "vx_binary_run.h" diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-u8.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-u8.c new file mode 100644 index 0000000..a0e80b7 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vx_vf/vx_vadd-run-1-u8.c @@ -0,0 +1,14 @@ +/* { dg-do run { target { riscv_v } } } */ +/* { dg-additional-options "-std=c99 --param=gpr2vr-cost=0" } */ + +#include "vx_binary.h" +#include "vx_binary_data.h" + +#define T uint8_t + +DEF_VX_BINARY_WRAP(T, +) + +#define TEST_DATA TEST_BINARY_DATA_WRAP(T, vadd) +#define TEST_RUN(out, in, x, n) RUN_VX_BINARY_WRAP(out, in, x, n) + +#include "vx_binary_run.h" diff --git a/gcc/testsuite/gcc.target/riscv/rvv/rvv.exp b/gcc/testsuite/gcc.target/riscv/rvv/rvv.exp index 3824997..4283d12 100644 --- a/gcc/testsuite/gcc.target/riscv/rvv/rvv.exp +++ b/gcc/testsuite/gcc.target/riscv/rvv/rvv.exp @@ -130,6 +130,8 @@ foreach op $AUTOVEC_TEST_OPTS { "$op" "" dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/autovec/sat/*.\[cS\]]] \ "$op" "" + dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/autovec/vx_vf/*.\[cS\]]] \ + "$op" "" } # All done. diff --git a/gcc/testsuite/gfortran.dg/interface_60.f90 b/gcc/testsuite/gfortran.dg/interface_60.f90 new file mode 100644 index 0000000..a7701f6 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/interface_60.f90 @@ -0,0 +1,70 @@ +! { dg-do run } +! { dg-options "-Wexternal-argument-mismatch" } +! Originally proc_ptr_52.f90, this gave an error with the warning above. + +module cs + +implicit none + +integer, target :: integer_target + +abstract interface + function classStar_map_ifc(x) result(y) + class(*), pointer :: y + class(*), target, intent(in) :: x + end function classStar_map_ifc +end interface + +contains + + function fun(x) result(y) + class(*), pointer :: y + class(*), target, intent(in) :: x + select type (x) + type is (integer) + integer_target = x ! Deals with dangling target. + y => integer_target + class default + y => null() + end select + end function fun + + function apply(fap, x) result(y) + procedure(classStar_map_ifc) :: fap + integer, intent(in) :: x + integer :: y + class(*), pointer :: p + y = 0 ! Get rid of 'y' undefined warning + p => fap (x) + select type (p) + type is (integer) + y = p + end select + end function apply + + function selector() result(fsel) + procedure(classStar_map_ifc), pointer :: fsel + fsel => fun + end function selector + +end module cs + + +program classStar_map + +use cs +implicit none + +integer :: x, y +procedure(classStar_map_ifc), pointer :: fm + +x = 123654 +fm => selector () ! Fixed by second chunk in patch +y = apply (fm, x) ! Fixed by first chunk in patch +if (x .ne. y) stop 1 + +x = 2 * x +y = apply (fun, x) ! PR93925; fixed as above +if (x .ne. y) stop 2 + +end program classStar_map diff --git a/gcc/testsuite/gfortran.dg/pr120049_a.f90 b/gcc/testsuite/gfortran.dg/pr120049_a.f90 new file mode 100644 index 0000000..c404a4d --- /dev/null +++ b/gcc/testsuite/gfortran.dg/pr120049_a.f90 @@ -0,0 +1,15 @@ +! { dg-do preprocess } +! { dg-additional-options "-cpp" } +! +! Test the fix for PR86248 +program tests_gtk_sup + use gtk_sup + implicit none + type(c_ptr), target :: val + if (c_associated(val, c_loc(val))) then + stop 1 + endif + if (c_associated(c_loc(val), val)) then + stop 2 + endif +end program tests_gtk_sup diff --git a/gcc/testsuite/gfortran.dg/pr120049_b.f90 b/gcc/testsuite/gfortran.dg/pr120049_b.f90 new file mode 100644 index 0000000..127db98 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/pr120049_b.f90 @@ -0,0 +1,8 @@ +! { dg-do run } +! { dg-additional-sources pr120049_a.f90 } +! +! Module for pr120049.f90 +! +module gtk_sup + use, intrinsic :: iso_c_binding +end module gtk_sup diff --git a/gcc/testsuite/libgdiagnostics.dg/test-logical-location-c.py b/gcc/testsuite/libgdiagnostics.dg/test-logical-location-c.py index 7448a1e..55f338b 100644 --- a/gcc/testsuite/libgdiagnostics.dg/test-logical-location-c.py +++ b/gcc/testsuite/libgdiagnostics.dg/test-logical-location-c.py @@ -31,7 +31,16 @@ def test_sarif_output_with_logical_location(sarif): assert len(location['logicalLocations']) == 1 logical_loc = location['logicalLocations'][0] + assert logical_loc['index'] == 0 + assert logical_loc['fullyQualifiedName'] == 'test_qualified_name' + + # Check theRun.logicalLocations + assert 'logicalLocations' in run + assert len(run['logicalLocations']) == 1 + logical_loc = run['logicalLocations'][0] assert logical_loc['name'] == 'test_short_name' assert logical_loc['fullyQualifiedName'] == 'test_qualified_name' assert logical_loc['decoratedName'] == 'test_decorated_name' assert logical_loc['kind'] == 'function' + assert logical_loc['index'] == 0 + diff --git a/gcc/testsuite/libgdiagnostics.dg/test-logical-location.c b/gcc/testsuite/libgdiagnostics.dg/test-logical-location.c index 1408919..b59ece4 100644 --- a/gcc/testsuite/libgdiagnostics.dg/test-logical-location.c +++ b/gcc/testsuite/libgdiagnostics.dg/test-logical-location.c @@ -20,6 +20,7 @@ PRINT "hello world!"; const int line_num = __LINE__ - 2; #include <assert.h> +#include <string.h> int main () @@ -62,6 +63,17 @@ main () diagnostic_finish (d, "can't find %qs", "foo"); /* end quoted source */ + /* Verify that the accessors work. */ + assert (diagnostic_logical_location_get_kind (logical_loc) + == DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION); + assert (!diagnostic_logical_location_get_parent (logical_loc)); + assert (!strcmp (diagnostic_logical_location_get_short_name (logical_loc), + "test_short_name")); + assert (!strcmp (diagnostic_logical_location_get_fully_qualified_name (logical_loc), + "test_qualified_name")); + assert (!strcmp (diagnostic_logical_location_get_decorated_name (logical_loc), + "test_decorated_name")); + /* Verify that creating a diagnostic_logical_location with equal values yields the same instance. */ const diagnostic_logical_location *dup diff --git a/gcc/testsuite/libgdiagnostics.dg/test-logical-location.cc b/gcc/testsuite/libgdiagnostics.dg/test-logical-location.cc new file mode 100644 index 0000000..3080ade --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-logical-location.cc @@ -0,0 +1,91 @@ +/* C++ example of using a logical location. + + Intended output is similar to: + +In function 'test_qualified_name': +PATH/test-error-with-note.cc:17:8: error: can't find 'foo' + 17 | PRINT "hello world!"; + | ^~~~~~~~~~~~ + + along with the equivalent in SARIF. */ + +#include "libgdiagnostics++.h" + +/* Placeholder source: +_________111111111122 +123456789012345678901 +PRINT "hello world!"; +*/ +const int line_num = __LINE__ - 2; + +#include <assert.h> +#include <string.h> + +int +main () +{ + libgdiagnostics::manager mgr; + + auto file = mgr.new_file (__FILE__, "c"); + + mgr.add_text_sink (stderr, DIAGNOSTIC_COLORIZE_IF_TTY); + + auto loc_start = mgr.new_location_from_file_line_column (file, line_num, 8); + auto loc_end = mgr.new_location_from_file_line_column (file, line_num, 19); + auto loc_range = mgr.new_location_from_range (loc_start, loc_start, loc_end); + + auto err (mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_ERROR)); + err.set_location (loc_range); + + libgdiagnostics::logical_location logical_loc + = mgr.new_logical_location (DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION, + NULL, /* parent */ + "test_short_name", + "test_qualified_name", + "test_decorated_name"); + err.set_logical_location (logical_loc); + + err.finish ("can't find %qs", "foo"); + + /* Verify that the accessors work. */ + assert (logical_loc.get_kind () + == DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION); + assert (logical_loc.get_parent ().m_inner == nullptr); + assert (!strcmp (logical_loc.get_short_name (), + "test_short_name")); + assert (!strcmp (logical_loc.get_fully_qualified_name (), + "test_qualified_name")); + assert (!strcmp (logical_loc.get_decorated_name (), + "test_decorated_name")); + + /* Verify that libgdiagnostic::logical_location instances created with + equal values compare as equal. */ + libgdiagnostics::logical_location dup + = mgr.new_logical_location (DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION, + NULL, /* parent */ + "test_short_name", + "test_qualified_name", + "test_decorated_name"); + assert (dup == logical_loc); + + /* Verify that libgdiagnostic::logical_location instances created with + differing values compare as non-equal. */ + libgdiagnostics::logical_location other + = mgr.new_logical_location (DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION, + NULL, /* parent */ + "something_else", + NULL, NULL); + assert (other != logical_loc); + + return 0; +} + +/* Check the output from the text sink. */ +/* { dg-begin-multiline-output "" } +In function 'test_qualified_name': + { dg-end-multiline-output "" } */ +/* { dg-regexp "\[^\n\r\]+test-logical-location.cc:17:8: error: can't find 'foo'" } */ +/* { dg-begin-multiline-output "" } + 17 | PRINT "hello world!"; + | ^~~~~~~~~~~~ + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-nested-logical-locations-json-c.py b/gcc/testsuite/libgdiagnostics.dg/test-nested-logical-locations-json-c.py new file mode 100644 index 0000000..39cc1a9 --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-nested-logical-locations-json-c.py @@ -0,0 +1,79 @@ +from sarif import * + +import pytest + +@pytest.fixture(scope='function', autouse=True) +def sarif(): + return sarif_from_env() + +def test_sarif_output_with_logical_location(sarif): + schema = sarif['$schema'] + assert schema == 'https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json' + + version = sarif['version'] + assert version == '2.1.0' + + runs = sarif['runs'] + run = runs[0] + + tool = run['tool'] + assert tool['driver']['name'] == 'test-nested-logical-locations-json.c.exe' + + results = run['results'] + assert len(results) == 2 + + result = results[0] + assert result['ruleId'] == 'warning' + assert result['level'] == 'warning' + assert result['message']['text'] == "product ID is blank" + assert len(result['locations']) == 1 + location = result['locations'][0] + assert len(location['logicalLocations']) == 1 + logical_loc = location['logicalLocations'][0] + assert logical_loc['index'] == 3 + assert logical_loc['fullyQualifiedName'] == '/orders/0/productIds/1' + + result = results[1] + assert result['ruleId'] == 'warning' + assert result['level'] == 'warning' + assert result['message']['text'] == "value is negative" + assert len(result['locations']) == 1 + location = result['locations'][0] + assert len(location['logicalLocations']) == 1 + logical_loc = location['logicalLocations'][0] + assert logical_loc['index'] == 4 + assert logical_loc['fullyQualifiedName'] == '/orders/0/total' + + # Check theRun.logicalLocations + assert 'logicalLocations' in run + assert len(run['logicalLocations']) == 5 + logical_loc = run['logicalLocations'][0] + assert logical_loc['name'] == 'orders' + assert logical_loc['fullyQualifiedName'] == '/orders' + assert logical_loc['kind'] == 'array' + assert logical_loc['index'] == 0 + logical_loc = run['logicalLocations'][1] + assert logical_loc['name'] == '0' + assert logical_loc['fullyQualifiedName'] == '/orders/0' + assert logical_loc['kind'] == 'object' + assert logical_loc['parentIndex'] == 0 + assert logical_loc['index'] == 1 + logical_loc = run['logicalLocations'][2] + assert logical_loc['name'] == 'productIds' + assert logical_loc['fullyQualifiedName'] == '/orders/0/productIds' + assert logical_loc['kind'] == 'array' + assert logical_loc['parentIndex'] == 1 + assert logical_loc['index'] == 2 + logical_loc = run['logicalLocations'][3] + assert logical_loc['name'] == '1' + assert logical_loc['fullyQualifiedName'] == '/orders/0/productIds/1' + assert logical_loc['kind'] == 'value' + assert logical_loc['parentIndex'] == 2 + assert logical_loc['index'] == 3 + logical_loc = run['logicalLocations'][4] + assert logical_loc['name'] == 'total' + assert logical_loc['fullyQualifiedName'] == '/orders/0/total' + assert logical_loc['kind'] == 'property' + assert logical_loc['parentIndex'] == 1 + assert logical_loc['index'] == 4 + diff --git a/gcc/testsuite/libgdiagnostics.dg/test-nested-logical-locations-json.c b/gcc/testsuite/libgdiagnostics.dg/test-nested-logical-locations-json.c new file mode 100644 index 0000000..d06450e --- /dev/null +++ b/gcc/testsuite/libgdiagnostics.dg/test-nested-logical-locations-json.c @@ -0,0 +1,165 @@ +/* Example of nested logical locations, based on the JSON example in + SARIF v2.1.0, 3.33.7 "kind" property; + though see https://github.com/oasis-tcs/sarif-spec/issues/670 + + Intended output is similar to: + +In JSON value '/orders/0/productIds/1': +PATH/test-nested-logical-locations-json.c:28:32: warning: product ID is blank + 28 | "productIds": [ "A-101", "", "A-223" ], + | ^~ +In JSON property '/orders/0/total': +PATH/test-nested-logical-locations-json.c:29:16: warning: value is negative + 29 | "total": "-3.25" + | ^~~~~~~ + + along with the equivalent in SARIF, capturing JSON structure + as nested logical locations. */ + +#include "libgdiagnostics.h" +#include "test-helpers.h" + +/* Placeholder source: +_________1111111111222222222233333333334444444444 +1234567890123456789012345678901234567890123456789 +{ + "orders": [ + { + "productIds": [ "A-101", "", "A-223" ], + "total": "-3.25" + } + ] +} +*/ +const int start_line_num = __LINE__ - 9; +const int line_num_of_product_ids = start_line_num + 3; +const int line_num_of_total = line_num_of_product_ids + 1; + +#include <assert.h> + +int +main () +{ + begin_test ("test-nested-logical-locations-json.c.exe", + "test-nested-logical-locations-json.c.sarif", + __FILE__, "c"); + + /* Create tree of logical locations. */ + /* begin quoted source */ + const diagnostic_logical_location *logical_loc_orders_arr + = diagnostic_manager_new_logical_location (diag_mgr, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY, + NULL, /* parent */ + "orders", + "/orders", + NULL); + const diagnostic_logical_location *logical_loc_order_0 + = diagnostic_manager_new_logical_location (diag_mgr, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_OBJECT, + logical_loc_orders_arr, /* parent */ + "0", + "/orders/0", + NULL); + const diagnostic_logical_location *logical_loc_product_ids + = diagnostic_manager_new_logical_location (diag_mgr, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_ARRAY, + logical_loc_order_0, /* parent */ + "productIds", + "/orders/0/productIds", + NULL); + const diagnostic_logical_location *logical_loc_element_1 + = diagnostic_manager_new_logical_location (diag_mgr, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_VALUE, + logical_loc_product_ids, /* parent */ + "1", + "/orders/0/productIds/1", + NULL); + const diagnostic_logical_location *logical_loc_total + = diagnostic_manager_new_logical_location (diag_mgr, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_PROPERTY, + logical_loc_order_0, /* parent */ + "total", + "/orders/0/total", + NULL); + /* end quoted source */ + + { + const int line_num = line_num_of_product_ids; + const diagnostic_physical_location *loc_start + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, + main_file, + line_num, + 32); + const diagnostic_physical_location *loc_end + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, + main_file, + line_num, + 33); + const diagnostic_physical_location *loc_range + = diagnostic_manager_new_location_from_range (diag_mgr, + loc_start, + loc_start, + loc_end); + + diagnostic *d = diagnostic_begin (diag_mgr, + DIAGNOSTIC_LEVEL_WARNING); + diagnostic_set_location (d, loc_range); + + diagnostic_set_logical_location (d, logical_loc_element_1); + + diagnostic_finish (d, "product ID is blank"); + } + { + const int line_num = line_num_of_total; + const diagnostic_physical_location *loc_start + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, + main_file, + line_num, + 16); + const diagnostic_physical_location *loc_end + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, + main_file, + line_num, + 22); + const diagnostic_physical_location *loc_range + = diagnostic_manager_new_location_from_range (diag_mgr, + loc_start, + loc_start, + loc_end); + + diagnostic *d = diagnostic_begin (diag_mgr, + DIAGNOSTIC_LEVEL_WARNING); + diagnostic_set_location (d, loc_range); + + diagnostic_set_logical_location (d, logical_loc_total); + + diagnostic_finish (d, "value is negative"); + } + + return end_test (); +} + +/* Check the output from the text sink. */ +/* { dg-begin-multiline-output "" } +In JSON value '/orders/0/productIds/1': + { dg-end-multiline-output "" } */ +/* { dg-regexp "\[^\n\r\]+test-nested-logical-locations-json.c:28:32: warning: product ID is blank" } */ +/* { dg-begin-multiline-output "" } + 28 | "productIds": [ "A-101", "", "A-223" ], + | ^~ + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } +In JSON property '/orders/0/total': + { dg-end-multiline-output "" } */ +/* { dg-regexp "\[^\n\r\]+test-nested-logical-locations-json.c:29:16: warning: value is negative" } */ +/* { dg-begin-multiline-output "" } + 29 | "total": "-3.25" + | ^~~~~~~ + { dg-end-multiline-output "" } */ + +/* Verify that some JSON was written to a file with the expected name: + { dg-final { verify-sarif-file } } */ + +/* Use a Python script to verify various properties about the generated + .sarif file: + { dg-final { run-sarif-pytest test-nested-logical-locations-json.c "test-nested-logical-locations-json-c.py" } } */ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-warning-with-path-c.py b/gcc/testsuite/libgdiagnostics.dg/test-warning-with-path-c.py index 5d3bbc4..af1e7b9 100644 --- a/gcc/testsuite/libgdiagnostics.dg/test-warning-with-path-c.py +++ b/gcc/testsuite/libgdiagnostics.dg/test-warning-with-path-c.py @@ -51,10 +51,8 @@ def test_sarif_output_for_warning_with_path(sarif): assert len(location['logicalLocations']) == 1 logical_loc = location['logicalLocations'][0] - assert logical_loc['name'] == 'make_a_list_of_random_ints_badly' + assert logical_loc['index'] == 0 assert logical_loc['fullyQualifiedName'] == 'make_a_list_of_random_ints_badly' - assert logical_loc['decoratedName'] == 'make_a_list_of_random_ints_badly' - assert logical_loc['kind'] == 'function' assert len(result['codeFlows']) == 1 assert len(result['codeFlows'][0]['threadFlows']) == 1 @@ -106,3 +104,13 @@ def test_sarif_output_for_warning_with_path(sarif): == "when calling 'PyList_Append', passing NULL from (1) as argument 1" assert tfl_2['nestingLevel'] == 0 assert tfl_2['executionOrder'] == 3 + + # Check theRun.logicalLocations + assert 'logicalLocations' in run + assert len(run['logicalLocations']) == 1 + logical_loc = run['logicalLocations'][0] + assert logical_loc['name'] == 'make_a_list_of_random_ints_badly' + assert logical_loc['fullyQualifiedName'] == 'make_a_list_of_random_ints_badly' + assert logical_loc['decoratedName'] == 'make_a_list_of_random_ints_badly' + assert logical_loc['kind'] == 'function' + assert logical_loc['index'] == 0 diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.1-not-an-object.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.1-not-an-object.sarif index 4743bad..9f0b87b 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.1-not-an-object.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.1-not-an-object.sarif @@ -1,6 +1,9 @@ [ null ] // { dg-error "expected a sarifLog object as the top-level value \\\[SARIF v2.1.0 §3.1\\\]" } /* { dg-begin-multiline-output "" } +In JSON array '': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 1 | [ null ] | ^~~~~~~~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.11-missing-arguments-for-placeholders.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.11-missing-arguments-for-placeholders.sarif index c4354d4..be2316a 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.11-missing-arguments-for-placeholders.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.11-missing-arguments-for-placeholders.sarif @@ -9,6 +9,9 @@ } /* { dg-begin-multiline-output "" } +In JSON object '/runs/0/results/0/message': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 6 | { "message": { "text" : "the {0} {1} fox jumps over the {2} dog" } } | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.11-not-enough-arguments-for-placeholders.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.11-not-enough-arguments-for-placeholders.sarif index e3eb534..98c3dc9 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.11-not-enough-arguments-for-placeholders.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.11-not-enough-arguments-for-placeholders.sarif @@ -9,6 +9,9 @@ } /* { dg-begin-multiline-output "" } +In JSON object '/runs/0/results/0/message': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 6 | { "message": { "text" : "the {0} {1} fox jumps over the {2} dog", "arguments": ["quick", "brown"] } } | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.5-unescaped-braces.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.5-unescaped-braces.sarif index 29460e1..f816859 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.5-unescaped-braces.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.11.5-unescaped-braces.sarif @@ -10,6 +10,9 @@ } /* { dg-begin-multiline-output "" } +In JSON property '/runs/0/results/0/message/text': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 6 | { "message": { "text" : "before {} after" }, | ^~~~~~~~~~~~~~~~~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.2-no-version.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.2-no-version.sarif index 771bd9c..d290604 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.2-no-version.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.2-no-version.sarif @@ -1,6 +1,9 @@ { } // { dg-error "expected sarifLog object to have a 'version' property \\\[SARIF v2.1.0 §3.13.2\\\]" } /* { dg-begin-multiline-output "" } +In JSON object '': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 1 | { } | ^~~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.2-version-not-a-string.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.2-version-not-a-string.sarif index 87bfd0d..c83be50 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.2-version-not-a-string.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.2-version-not-a-string.sarif @@ -1,6 +1,9 @@ { "version" : 42 } // { dg-error "15: expected sarifLog.version to be a JSON string; got JSON number \\\[SARIF v2.1.0 §3.13.2\\\]" } /* { dg-begin-multiline-output "" } +In JSON property '/version': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 1 | { "version" : 42 } | ^~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-bad-runs.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-bad-runs.sarif index c5a26f0..202698e 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-bad-runs.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-bad-runs.sarif @@ -2,6 +2,9 @@ "runs": 42 } // { dg-error "expected sarifLog.runs to be 'null' or an array \\\[SARIF v2.1.0 §3.13.4\\\]" } /* { dg-begin-multiline-output "" } +In JSON property '/runs': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 2 | "runs": 42 } | ^~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-no-runs.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-no-runs.sarif index f142321..a4870c1 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-no-runs.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-no-runs.sarif @@ -1,6 +1,9 @@ { "version": "2.1.0" } // { dg-error "expected sarifLog object to have a 'runs' property \\\[SARIF v2.1.0 §3.13.4\\\]" } /* { dg-begin-multiline-output "" } +In JSON object '': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 1 | { "version": "2.1.0" } | ^~~~~~~~~~~~~~~~~~~~~~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-non-object-in-runs.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-non-object-in-runs.sarif index 4eeaaaa..b870959 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-non-object-in-runs.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.13.4-non-object-in-runs.sarif @@ -2,6 +2,9 @@ "runs" : [42] } // { dg-error "expected element of sarifLog.runs array to be an object \\\[SARIF v2.1.0 §3.13.4\\\]" } /* { dg-begin-multiline-output "" } +In JSON property '/runs/0': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 2 | "runs" : [42] } | ^~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.27.10-bad-level.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.27.10-bad-level.sarif index b5c3fbe..eb60b66 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.27.10-bad-level.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.27.10-bad-level.sarif @@ -20,6 +20,9 @@ } /* { dg-begin-multiline-output "" } +In JSON property '/runs/0/results/0/level': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 12 | "level": "mostly harmless", | ^~~~~~~~~~~~~~~~~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.33.3-index-out-of-range.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.33.3-index-out-of-range.sarif index ef22614..d7baac9 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.33.3-index-out-of-range.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.33.3-index-out-of-range.sarif @@ -27,6 +27,9 @@ } /* { dg-begin-multiline-output "" } +In JSON property '/runs/0/results/0/locations/0/logicalLocations/0/index' + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 17 | "index": 42 | ^~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-unhandled/3.27.10-none-level.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-unhandled/3.27.10-none-level.sarif index 7f58677..20552a3 100644 --- a/gcc/testsuite/sarif-replay.dg/2.1.0-unhandled/3.27.10-none-level.sarif +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-unhandled/3.27.10-none-level.sarif @@ -20,6 +20,9 @@ } /* { dg-begin-multiline-output "" } +In JSON property '/runs/0/results/0/level' + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } 12 | "level": "none", | ^~~~~~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.33.7-json-example.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.33.7-json-example.sarif new file mode 100644 index 0000000..6e4cf5b --- /dev/null +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.33.7-json-example.sarif @@ -0,0 +1,83 @@ +/* Adapted from the JSON example in SARIF v2.1.0, 3.33.7 "kind" property; + see https://github.com/oasis-tcs/sarif-spec/issues/670 */ +{ + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "example JSON scanner" + } + }, + "results": [ + { + "message": {"text": "product ID is blank"}, + "locations": [ + { + "logicalLocations": [ + { + "fullyQualifiedName": "/orders/0/productIds/1", + "index": 3 + } + ] + } + ] + }, + { + "message": {"text": "value is negative"}, + "locations": [ + { + "logicalLocations": [ + { + "fullyQualifiedName": "/orders/0/total", + "index": 4 + } + ] + } + ] + } + ], + "logicalLocations": [ + { + "name": "orders", + "fullyQualifiedName": "/orders", + "kind": "array" + }, + { + "name": "0", + "fullyQualifiedName": "/orders/0", + "kind": "object", + "parentIndex": 0 + }, + { + "name": "productIds", + "fullyQualifiedName": "/orders/0/productIds", + "kind": "array", + "parentIndex": 1 + }, + { + "name": "1", + "fullyQualifiedName": "/orders/0/productIds/1", + "kind": "value", + "parentIndex": 2 + }, + { + "name": "total", + "fullyQualifiedName": "/orders/0/total", + "kind": "property", + "parentIndex": 1 + } + ] + } + ] +} + +/* { dg-begin-multiline-output "" } +In JSON value '/orders/0/productIds/1': +example JSON scanner: warning: product ID is blank + { dg-end-multiline-output "" } */ + +/* { dg-begin-multiline-output "" } +In JSON property '/orders/0/total': +example JSON scanner: warning: value is negative + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.33.7-xml-example.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.33.7-xml-example.sarif new file mode 100644 index 0000000..36afc65 --- /dev/null +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.33.7-xml-example.sarif @@ -0,0 +1,77 @@ +/* Adapted from the XML example in SARIF v2.1.0, 3.33.7 "kind" property; + see also https://github.com/oasis-tcs/sarif-spec/issues/669 */ +{ + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "example XML scanner" + } + }, + "results": [ + { + "message": {"text": "empty value"}, + "locations": [ + { + "logicalLocations": [ + { + "fullyQualifiedName": "/orders/order[1]/@number", + "index": 2 + } + ] + } + ] + }, + { + "message": {"text": "total is negative"}, + "locations": [ + { + "logicalLocations": [ + { + "fullyQualifiedName": "/orders/order[1]/total/text()", + "index": 3 + } + ] + } + ] + } + ], + "logicalLocations": [ + { + "name": "orders", + "fullyQualifiedName": "/orders", + "kind": "element" + }, + { + "name": "order[1]", + "fullyQualifiedName": "/orders/order[1]", + "kind": "element", + "parentIndex": 0 + }, + { + "name": "number", + "fullyQualifiedName": "/orders/order[1]/@number", + "kind": "attribute", + "parentIndex": 1 + }, + { + "name": "text", + "fullyQualifiedName": "/orders/order[1]/total/text()", + "kind": "text", + "parentIndex": 1 + } + ] + } + ] +} + +/* { dg-begin-multiline-output "" } +In attribute '/orders/order[1]/@number': +example XML scanner: warning: empty value + { dg-end-multiline-output "" } */ + +/* { dg-begin-multiline-output "" } +In text '/orders/order[1]/total/text()': +example XML scanner: warning: total is negative + { dg-end-multiline-output "" } */ diff --git a/gcc/tree-diagnostic-client-data-hooks.cc b/gcc/tree-diagnostic-client-data-hooks.cc index 11701f5..06a0894 100644 --- a/gcc/tree-diagnostic-client-data-hooks.cc +++ b/gcc/tree-diagnostic-client-data-hooks.cc @@ -123,12 +123,14 @@ public: return &m_version_info; } - const logical_location *get_current_logical_location () const final override + const logical_location_manager *get_logical_location_manager () const final override { - if (current_function_decl) - return &m_current_fndecl_logical_loc; - else - return NULL; + return &m_logical_location_manager; + } + + logical_location_manager::key get_current_logical_location () const final override + { + return m_logical_location_manager.key_from_tree (current_function_decl); } const char * @@ -159,7 +161,7 @@ public: private: compiler_version_info m_version_info; - current_fndecl_logical_location m_current_fndecl_logical_loc; + tree_logical_location_manager m_logical_location_manager; }; /* Create a compiler_data_hooks (so that the class can be local diff --git a/gcc/tree-logical-location.cc b/gcc/tree-logical-location.cc index 8f677a3..1b2702f 100644 --- a/gcc/tree-logical-location.cc +++ b/gcc/tree-logical-location.cc @@ -1,4 +1,4 @@ -/* Subclasses of logical_location with knowledge of "tree". +/* Subclass of logical_location_manager with knowledge of "tree". Copyright (C) 2022-2025 Free Software Foundation, Inc. Contributed by David Malcolm <dmalcolm@redhat.com>. @@ -27,50 +27,67 @@ along with GCC; see the file COPYING3. If not see #include "langhooks.h" #include "intl.h" -/* class compiler_logical_location : public logical_location. */ +static void +assert_valid_tree (const_tree node) +{ + gcc_assert (node); + gcc_assert (DECL_P (node) || TYPE_P (node)); + gcc_assert (TREE_CODE (node) != TRANSLATION_UNIT_DECL); +} -/* Get a string for DECL suitable for use by the SARIF logicalLocation - "name" property (SARIF v2.1.0 section 3.33.4). */ +/* class tree_logical_location_manager : public logical_location_manager. */ const char * -compiler_logical_location::get_short_name_for_tree (tree decl) +tree_logical_location_manager::get_short_name (key k) const { - gcc_assert (decl); - return identifier_to_locale (lang_hooks.decl_printable_name (decl, 0)); + tree node = tree_from_key (k); + assert_valid_tree (node); + + if (DECL_P (node)) + return identifier_to_locale (lang_hooks.decl_printable_name (node, 0)); + if (TYPE_P (node)) + return IDENTIFIER_POINTER (TYPE_IDENTIFIER (node)); + return nullptr; } -/* Get a string for DECL suitable for use by the SARIF logicalLocation - "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */ - const char * -compiler_logical_location::get_name_with_scope_for_tree (tree decl) +tree_logical_location_manager::get_name_with_scope (key k) const { - gcc_assert (decl); - return identifier_to_locale (lang_hooks.decl_printable_name (decl, 1)); + tree node = tree_from_key (k); + assert_valid_tree (node); + + if (DECL_P (node)) + return identifier_to_locale (lang_hooks.decl_printable_name (node, 1)); + if (TYPE_P (node)) + return nullptr; + return nullptr; } -/* Get a string for DECL suitable for use by the SARIF logicalLocation - "decoratedName" property (SARIF v2.1.0 section 3.33.6). */ - const char * -compiler_logical_location::get_internal_name_for_tree (tree decl) +tree_logical_location_manager::get_internal_name (key k) const { - gcc_assert (decl); - if (HAS_DECL_ASSEMBLER_NAME_P (decl)) - if (tree id = DECL_ASSEMBLER_NAME (decl)) - return IDENTIFIER_POINTER (id); + tree node = tree_from_key (k); + assert_valid_tree (node); + + if (DECL_P (node)) + { + if (HAS_DECL_ASSEMBLER_NAME_P (node) + && TREE_CODE (node) != NAMESPACE_DECL) // FIXME + if (tree id = DECL_ASSEMBLER_NAME (node)) + return IDENTIFIER_POINTER (id); + } + else if (TYPE_P (node)) + return nullptr; return NULL; } -/* Get what kind of SARIF logicalLocation DECL is (if any). */ - enum logical_location_kind -compiler_logical_location::get_kind_for_tree (tree decl) +tree_logical_location_manager::get_kind (key k) const { - if (!decl) - return LOGICAL_LOCATION_KIND_UNKNOWN; + tree node = tree_from_key (k); + assert_valid_tree (node); - switch (TREE_CODE (decl)) + switch (TREE_CODE (node)) { default: return LOGICAL_LOCATION_KIND_UNKNOWN; @@ -80,94 +97,51 @@ compiler_logical_location::get_kind_for_tree (tree decl) return LOGICAL_LOCATION_KIND_PARAMETER; case VAR_DECL: return LOGICAL_LOCATION_KIND_VARIABLE; - } -} - -label_text -compiler_logical_location::get_name_for_tree_for_path_output (tree decl) -{ - gcc_assert (decl); - const char *n = DECL_NAME (decl) - ? identifier_to_locale (lang_hooks.decl_printable_name (decl, 2)) - : _("<anonymous>"); - return label_text::borrow (n); -} - -/* class tree_logical_location : public compiler_logical_location. */ - -/* Implementation of the logical_location vfuncs, using m_decl. */ - -const char * -tree_logical_location::get_short_name () const -{ - gcc_assert (m_decl); - return get_short_name_for_tree (m_decl); -} - -const char * -tree_logical_location::get_name_with_scope () const -{ - gcc_assert (m_decl); - return get_name_with_scope_for_tree (m_decl); -} - -const char * -tree_logical_location::get_internal_name () const -{ - gcc_assert (m_decl); - return get_internal_name_for_tree (m_decl); -} + case NAMESPACE_DECL: + return LOGICAL_LOCATION_KIND_NAMESPACE; -enum logical_location_kind -tree_logical_location::get_kind () const -{ - gcc_assert (m_decl); - return get_kind_for_tree (m_decl); + case RECORD_TYPE: + return LOGICAL_LOCATION_KIND_TYPE; + } } label_text -tree_logical_location::get_name_for_path_output () const -{ - gcc_assert (m_decl); - return get_name_for_tree_for_path_output (m_decl); -} - -/* class current_fndecl_logical_location : public compiler_logical_location. */ - -/* Implementation of the logical_location vfuncs, using - current_function_decl. */ - -const char * -current_fndecl_logical_location::get_short_name () const +tree_logical_location_manager::get_name_for_path_output (key k) const { - gcc_assert (current_function_decl); - return get_short_name_for_tree (current_function_decl); -} + tree node = tree_from_key (k); + assert_valid_tree (node); -const char * -current_fndecl_logical_location::get_name_with_scope () const -{ - gcc_assert (current_function_decl); - return get_name_with_scope_for_tree (current_function_decl); -} - -const char * -current_fndecl_logical_location::get_internal_name () const -{ - gcc_assert (current_function_decl); - return get_internal_name_for_tree (current_function_decl); + if (DECL_P (node)) + { + const char *n = DECL_NAME (node) + ? identifier_to_locale (lang_hooks.decl_printable_name (node, 2)) + : _("<anonymous>"); + return label_text::borrow (n); + } + else if (TYPE_P (node)) + return label_text (); + return label_text (); } -enum logical_location_kind -current_fndecl_logical_location::get_kind () const +logical_location +tree_logical_location_manager::get_parent (key k) const { - gcc_assert (current_function_decl); - return get_kind_for_tree (current_function_decl); -} + tree node = tree_from_key (k); + assert_valid_tree (node); -label_text -current_fndecl_logical_location::get_name_for_path_output () const -{ - gcc_assert (current_function_decl); - return get_name_for_tree_for_path_output (current_function_decl); + if (DECL_P (node)) + { + if (!DECL_CONTEXT (node)) + return logical_location (); + if (TREE_CODE (DECL_CONTEXT (node)) == TRANSLATION_UNIT_DECL) + return logical_location (); + return key_from_tree (DECL_CONTEXT (node)); + } + else if (TYPE_P (node)) + { + if (!TYPE_CONTEXT (node)) + return logical_location (); + return key_from_tree (TYPE_CONTEXT (node)); + } + return logical_location (); } diff --git a/gcc/tree-logical-location.h b/gcc/tree-logical-location.h index 1e15bee..0d57888 100644 --- a/gcc/tree-logical-location.h +++ b/gcc/tree-logical-location.h @@ -1,4 +1,4 @@ -/* Subclasses of logical_location with knowledge of "tree". +/* Subclass of logical_location_manager with knowledge of "tree". Copyright (C) 2022-2025 Free Software Foundation, Inc. Contributed by David Malcolm <dmalcolm@redhat.com>. @@ -23,48 +23,28 @@ along with GCC; see the file COPYING3. If not see #include "logical-location.h" -/* Abstract subclass of logical_location, with knowledge of "tree", but - for no specific tree. */ - -class compiler_logical_location : public logical_location -{ - protected: - static const char *get_short_name_for_tree (tree); - static const char *get_name_with_scope_for_tree (tree); - static const char *get_internal_name_for_tree (tree); - static enum logical_location_kind get_kind_for_tree (tree); - static label_text get_name_for_tree_for_path_output (tree); -}; - -/* Concrete subclass of logical_location, with reference to a specific - tree. */ - -class tree_logical_location : public compiler_logical_location -{ -public: - tree_logical_location (tree decl) : m_decl (decl) {} - - const char *get_short_name () const final override; - const char *get_name_with_scope () const final override; - const char *get_internal_name () const final override; - enum logical_location_kind get_kind () const final override; - label_text get_name_for_path_output () const final override; - -private: - tree m_decl; -}; - -/* Concrete subclass of logical_location, with reference to - current_function_decl. */ - -class current_fndecl_logical_location : public compiler_logical_location +/* A subclass of logical_location_manager in which the keys are + "tree". + Note that there is no integration with the garbage collector, + and so logical_location instances can only be short-lived. */ +class tree_logical_location_manager : public logical_location_manager { public: - const char *get_short_name () const final override; - const char *get_name_with_scope () const final override; - const char *get_internal_name () const final override; - enum logical_location_kind get_kind () const final override; - label_text get_name_for_path_output () const final override; + const char *get_short_name (key) const final override; + const char *get_name_with_scope (key) const final override; + const char *get_internal_name (key) const final override; + enum logical_location_kind get_kind (key) const final override; + label_text get_name_for_path_output (key) const final override; + key get_parent (key) const final override; + + static tree tree_from_key (logical_location k) + { + return const_cast<tree> (k.cast_to<const_tree> ()); + } + static logical_location key_from_tree (tree node) + { + return logical_location::from_ptr (node); + } }; #endif /* GCC_TREE_LOGICAL_LOCATION_H. */ diff --git a/gcc/tree-vect-data-refs.cc b/gcc/tree-vect-data-refs.cc index c9395e3..231a3ca 100644 --- a/gcc/tree-vect-data-refs.cc +++ b/gcc/tree-vect-data-refs.cc @@ -1203,6 +1203,97 @@ vect_slp_analyze_instance_dependence (vec_info *vinfo, slp_instance instance) for (unsigned k = 0; k < SLP_TREE_SCALAR_STMTS (store).length (); ++k) gimple_set_visited (SLP_TREE_SCALAR_STMTS (store)[k]->stmt, false); + /* If this is a SLP instance with a store check if there's a dependent + load that cannot be forwarded from a previous iteration of a loop + both are in. This is to avoid situations like that in PR115777. */ + if (res && store) + { + stmt_vec_info store_info + = DR_GROUP_FIRST_ELEMENT (SLP_TREE_SCALAR_STMTS (store)[0]); + class loop *store_loop = gimple_bb (store_info->stmt)->loop_father; + if (! loop_outer (store_loop)) + return res; + vec<loop_p> loop_nest; + loop_nest.create (1); + loop_nest.quick_push (store_loop); + data_reference *drs = nullptr; + for (slp_tree &load : SLP_INSTANCE_LOADS (instance)) + { + if (! STMT_VINFO_GROUPED_ACCESS (SLP_TREE_SCALAR_STMTS (load)[0])) + continue; + stmt_vec_info load_info + = DR_GROUP_FIRST_ELEMENT (SLP_TREE_SCALAR_STMTS (load)[0]); + if (gimple_bb (load_info->stmt)->loop_father != store_loop) + continue; + + /* For now concern ourselves with write-after-read as we also + only look for re-use of the store within the same SLP instance. + We can still get a RAW here when the instance contais a PHI + with a backedge though, thus this test. */ + if (! vect_stmt_dominates_stmt_p (STMT_VINFO_STMT (load_info), + STMT_VINFO_STMT (store_info))) + continue; + + if (! drs) + { + drs = create_data_ref (loop_preheader_edge (store_loop), + store_loop, + DR_REF (STMT_VINFO_DATA_REF (store_info)), + store_info->stmt, false, false); + if (! DR_BASE_ADDRESS (drs) + || TREE_CODE (DR_STEP (drs)) != INTEGER_CST) + break; + } + data_reference *drl + = create_data_ref (loop_preheader_edge (store_loop), + store_loop, + DR_REF (STMT_VINFO_DATA_REF (load_info)), + load_info->stmt, true, false); + + /* See whether the DRs have a known constant distance throughout + the containing loop iteration. */ + if (! DR_BASE_ADDRESS (drl) + || ! operand_equal_p (DR_STEP (drs), DR_STEP (drl)) + || ! operand_equal_p (DR_BASE_ADDRESS (drs), + DR_BASE_ADDRESS (drl)) + || ! operand_equal_p (DR_OFFSET (drs), DR_OFFSET (drl))) + { + free_data_ref (drl); + continue; + } + + /* If the next iteration load overlaps with a non-power-of-two offset + we are surely failing any STLF attempt. */ + HOST_WIDE_INT step = TREE_INT_CST_LOW (DR_STEP (drl)); + unsigned HOST_WIDE_INT sizes + = (TREE_INT_CST_LOW (TYPE_SIZE_UNIT (TREE_TYPE (DR_REF (drs)))) + * DR_GROUP_SIZE (store_info)); + unsigned HOST_WIDE_INT sizel + = (TREE_INT_CST_LOW (TYPE_SIZE_UNIT (TREE_TYPE (DR_REF (drl)))) + * DR_GROUP_SIZE (load_info)); + if (ranges_overlap_p (TREE_INT_CST_LOW (DR_INIT (drl)) + step, sizel, + TREE_INT_CST_LOW (DR_INIT (drs)), sizes)) + { + unsigned HOST_WIDE_INT dist + = absu_hwi (TREE_INT_CST_LOW (DR_INIT (drl)) + step + - TREE_INT_CST_LOW (DR_INIT (drs))); + poly_uint64 loadsz = tree_to_poly_uint64 + (TYPE_SIZE_UNIT (SLP_TREE_VECTYPE (load))); + poly_uint64 storesz = tree_to_poly_uint64 + (TYPE_SIZE_UNIT (SLP_TREE_VECTYPE (store))); + /* When the overlap aligns with vector sizes used for the loads + and the vector stores are larger or equal to the loads + forwarding should work. */ + if (maybe_gt (loadsz, storesz) || ! multiple_p (dist, loadsz)) + load->avoid_stlf_fail = true; + } + free_data_ref (drl); + } + if (drs) + free_data_ref (drs); + loop_nest.release (); + } + return res; } diff --git a/gcc/tree-vect-slp.cc b/gcc/tree-vect-slp.cc index 9bf142d..562e222 100644 --- a/gcc/tree-vect-slp.cc +++ b/gcc/tree-vect-slp.cc @@ -122,6 +122,7 @@ _slp_tree::_slp_tree () SLP_TREE_DEF_TYPE (this) = vect_uninitialized_def; SLP_TREE_CODE (this) = ERROR_MARK; this->ldst_lanes = false; + this->avoid_stlf_fail = false; SLP_TREE_VECTYPE (this) = NULL_TREE; SLP_TREE_REPRESENTATIVE (this) = NULL; SLP_TREE_MEMORY_ACCESS_TYPE (this) = VMAT_INVARIANT; @@ -3104,7 +3105,8 @@ vect_print_slp_tree (dump_flags_t dump_kind, dump_location_t loc, SLP_TREE_REF_COUNT (node)); if (SLP_TREE_VECTYPE (node)) dump_printf (metadata, " %T", SLP_TREE_VECTYPE (node)); - dump_printf (metadata, "\n"); + dump_printf (metadata, "%s\n", + node->avoid_stlf_fail ? " (avoid-stlf-fail)" : ""); if (SLP_TREE_DEF_TYPE (node) == vect_internal_def) { if (SLP_TREE_CODE (node) == VEC_PERM_EXPR) diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc index 537ae6c..ea0b426 100644 --- a/gcc/tree-vect-stmts.cc +++ b/gcc/tree-vect-stmts.cc @@ -2134,6 +2134,14 @@ get_group_load_store_type (vec_info *vinfo, stmt_vec_info stmt_info, : vect_store_lanes_supported (vectype, group_size, masked_p))) != IFN_LAST) *memory_access_type = VMAT_LOAD_STORE_LANES; + else if (!loop_vinfo && slp_node->avoid_stlf_fail) + { + *memory_access_type = VMAT_ELEMENTWISE; + if (dump_enabled_p ()) + dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, + "using element-wise load to avoid disrupting " + "cross iteration store-to-load forwarding\n"); + } else *memory_access_type = VMAT_CONTIGUOUS; diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h index 63991c3..3d11559 100644 --- a/gcc/tree-vectorizer.h +++ b/gcc/tree-vectorizer.h @@ -266,6 +266,9 @@ struct _slp_tree { /* Whether uses of this load or feeders of this store are suitable for load/store-lanes. */ bool ldst_lanes; + /* For BB vect, flag to indicate this load node should be vectorized + as to avoid STLF fails because of related stores. */ + bool avoid_stlf_fail; int vertex; |