> -----Original Message-----
> From: Tamar Christina
> Sent: 29 September 2025 12:59
> To: '[email protected]' <[email protected]>; gcc-
> [email protected]
> Cc: [email protected]; [email protected]; [email protected]
> Subject: RE: [v3 PATCH 4/6] aarch64: Enable parsing of user-provided
> AArch64 CPU tuning parameters
>
> Hi Soumya,
>
> > -----Original Message-----
> > From: [email protected] <[email protected]>
> > Sent: 25 August 2025 12:00
> > To: [email protected]
> > Cc: [email protected]; [email protected];
> [email protected]
> > Subject: [v3 PATCH 4/6] aarch64: Enable parsing of user-provided AArch64
> > CPU tuning parameters
> >
> > From: Soumya AR <[email protected]>
> >
> > This patch adds support for loading custom CPU tuning parameters from a
> > JSON
> > file for AArch64 targets. The '-muser-provided-CPU=' flag accepts a user
> > provided JSON file and overrides the internal tuning parameters at GCC
> > runtime.
> >
> > This patch was bootstrapped and regtested on aarch64-linux-gnu, no
> > regression.
> >
> > Signed-off-by: Soumya AR <[email protected]>
> >
> > gcc/ChangeLog:
> >
> > * config.gcc: Add aarch64-json-tunings-parser.o.
> > * config/aarch64/aarch64.cc (aarch64_override_options_internal):
> > Invoke
> > aarch64_load_tuning_params_from_json if -muser-provided-CPU= is
> > specified.
> > * config/aarch64/aarch64.opt: New option.
> > * config/aarch64/t-aarch64 (aarch64-json-tunings-parser.o): New
> > define.
> > * selftest-run-tests.cc (selftest::run_tests): Add json_tunings_tests().
> > * selftest.h (json_tunings_tests): Declare.
> > * config/aarch64/aarch64-json-schema.h: New file.
> > * config/aarch64/aarch64-json-tunings-parser.cc: New file.
> > * config/aarch64/aarch64-json-tunings-parser.h: New file.
> > ---
> > gcc/config.gcc | 2 +-
> > gcc/config/aarch64/aarch64-json-schema.h | 261 +++++
> > .../aarch64/aarch64-json-tunings-parser.cc | 902 ++++++++++++++++++
> > .../aarch64/aarch64-json-tunings-parser.h | 29 +
> > gcc/config/aarch64/aarch64.cc | 16 +
> > gcc/config/aarch64/aarch64.opt | 4 +
> > gcc/config/aarch64/t-aarch64 | 10 +
> > gcc/selftest-run-tests.cc | 1 +
> > gcc/selftest.h | 1 +
> > 9 files changed, 1225 insertions(+), 1 deletion(-)
> > create mode 100644 gcc/config/aarch64/aarch64-json-schema.h
> > create mode 100644 gcc/config/aarch64/aarch64-json-tunings-parser.cc
> > create mode 100644 gcc/config/aarch64/aarch64-json-tunings-parser.h
> >
> > diff --git a/gcc/config.gcc b/gcc/config.gcc
> > index 698a7bb4b73..39790adb16a 100644
> > --- a/gcc/config.gcc
> > +++ b/gcc/config.gcc
> > @@ -351,7 +351,7 @@ aarch64*-*-*)
> > c_target_objs="aarch64-c.o"
> > cxx_target_objs="aarch64-c.o"
> > d_target_objs="aarch64-d.o"
> > - extra_objs="aarch64-builtins.o aarch-common.o aarch64-elf-
> > metadata.o aarch64-sve-builtins.o aarch64-sve-builtins-shapes.o aarch64-
> > sve-builtins-base.o aarch64-sve-builtins-sve2.o aarch64-sve-builtins-sme.o
> > cortex-a57-fma-steering.o aarch64-speculation.o aarch-bti-insert.o
> aarch64-
> > early-ra.o aarch64-ldp-fusion.o aarch64-json-tunings-printer.o"
> > + extra_objs="aarch64-builtins.o aarch-common.o aarch64-elf-
> > metadata.o aarch64-sve-builtins.o aarch64-sve-builtins-shapes.o aarch64-
> > sve-builtins-base.o aarch64-sve-builtins-sve2.o aarch64-sve-builtins-sme.o
> > cortex-a57-fma-steering.o aarch64-speculation.o aarch-bti-insert.o
> aarch64-
> > early-ra.o aarch64-ldp-fusion.o aarch64-json-tunings-printer.o aarch64-
> json-
> > tunings-parser.o"
> > target_gtfiles="\$(srcdir)/config/aarch64/aarch64-protos.h
> > \$(srcdir)/config/aarch64/aarch64-builtins.h
> > \$(srcdir)/config/aarch64/aarch64-builtins.cc
> > \$(srcdir)/config/aarch64/aarch64-sve-builtins.h
> > \$(srcdir)/config/aarch64/aarch64-sve-builtins.cc"
> > target_has_targetm_common=yes
> > ;;
> > diff --git a/gcc/config/aarch64/aarch64-json-schema.h
> > b/gcc/config/aarch64/aarch64-json-schema.h
> > new file mode 100644
> > index 00000000000..873756b01a5
> > --- /dev/null
> > +++ b/gcc/config/aarch64/aarch64-json-schema.h
> > @@ -0,0 +1,261 @@
> > +/* Raw JSON schema for the AArch64 tuning parameters.
> > + Copyright The GNU Toolchain Authors.
>
> Same as the others the copyright year is missing.
>
> I'm still going through the code, but notice we don't write a
> gcc version out in the json. But the parser is rather unforgiving
> to mismatch in expected formats. (i.e. we ICE).
>
> I think we should write out the version, and reject reading in any
> jsons where the versions don't match with a friendly error message.
>
> Thanks,
> Tamar
>
> > +
> > + 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/>. */
> > +
> > +#ifndef AARCH64_JSON_SCHEMA_H
> > +#define AARCH64_JSON_SCHEMA_H
> > +
> > +static const char *schema_json = R"json(
> > +{
> > + "tune_params": {
> > + "insn_extra_cost": {
> > + "alu": {
> > + "arith": "int",
> > + "logical": "int",
> > + "shift": "int",
> > + "shift_reg": "int",
> > + "arith_shift": "int",
> > + "arith_shift_reg": "int",
> > + "log_shift": "int",
> > + "log_shift_reg": "int",
> > + "extend": "int",
> > + "extend_arith": "int",
> > + "bfi": "int",
> > + "bfx": "int",
> > + "clz": "int",
> > + "rev": "int",
> > + "non_exec": "int",
> > + "non_exec_costs_exec": "boolean"
> > + },
> > + "mult": [
> > + {
> > + "simple": "int",
> > + "flag_setting": "int",
> > + "extend": "int",
> > + "add": "int",
> > + "extend_add": "int",
> > + "idiv": "int"
> > + },
> > + {
> > + "simple": "int",
> > + "flag_setting": "int",
> > + "extend": "int",
> > + "add": "int",
> > + "extend_add": "int",
> > + "idiv": "int"
> > + }
> > + ],
> > + "ldst": {
> > + "load": "int",
> > + "load_sign_extend": "int",
> > + "ldrd": "int",
> > + "ldm_1st": "int",
> > + "ldm_regs_per_insn_1st": "int",
> > + "ldm_regs_per_insn_subsequent": "int",
> > + "loadf": "int",
> > + "loadd": "int",
> > + "load_unaligned": "int",
> > + "store": "int",
> > + "strd": "int",
> > + "stm_1st": "int",
> > + "stm_regs_per_insn_1st": "int",
> > + "stm_regs_per_insn_subsequent": "int",
> > + "storef": "int",
> > + "stored": "int",
> > + "store_unaligned": "int",
> > + "loadv": "int",
> > + "storev": "int"
> > + },
> > + "fp": [
> > + {
> > + "div": "int",
> > + "mult": "int",
> > + "mult_addsub": "int",
> > + "fma": "int",
> > + "addsub": "int",
> > + "fpconst": "int",
> > + "neg": "int",
> > + "compare": "int",
> > + "widen": "int",
> > + "narrow": "int",
> > + "toint": "int",
> > + "fromint": "int",
> > + "roundint": "int"
> > + },
> > + {
> > + "div": "int",
> > + "mult": "int",
> > + "mult_addsub": "int",
> > + "fma": "int",
> > + "addsub": "int",
> > + "fpconst": "int",
> > + "neg": "int",
> > + "compare": "int",
> > + "widen": "int",
> > + "narrow": "int",
> > + "toint": "int",
> > + "fromint": "int",
> > + "roundint": "int"
> > + }
> > + ],
> > + "vect": {
> > + "alu": "int",
> > + "mult": "int",
> > + "movi": "int",
> > + "dup": "int",
> > + "extract": "int"
> > + }
> > + },
> > + "addr_cost": {
> > + "addr_scale_costs": {
> > + "hi": "int",
> > + "si": "int",
> > + "di": "int",
> > + "ti": "int"
> > + },
> > + "pre_modify": "int",
> > + "post_modify": "int",
> > + "post_modify_ld3_st3": "int",
> > + "post_modify_ld4_st4": "int",
> > + "register_offset": "int",
> > + "register_sextend": "int",
> > + "register_zextend": "int",
> > + "imm_offset": "int"
> > + },
> > + "regmove_cost": {
> > + "GP2GP": "int",
> > + "GP2FP": "int",
> > + "FP2GP": "int",
> > + "FP2FP": "int"
> > + },
> > + "vec_costs": {
> > + "scalar_int_stmt_cost": "int",
> > + "scalar_fp_stmt_cost": "int",
> > + "scalar_load_cost": "int",
> > + "scalar_store_cost": "int",
> > + "cond_taken_branch_cost": "int",
> > + "cond_not_taken_branch_cost": "int",
> > + "advsimd": {
> > + "int_stmt_cost": "int",
> > + "fp_stmt_cost": "int",
> > + "ld2_st2_permute_cost": "int",
> > + "ld3_st3_permute_cost": "int",
> > + "ld4_st4_permute_cost": "int",
> > + "permute_cost": "int",
> > + "reduc_i8_cost": "int",
> > + "reduc_i16_cost": "int",
> > + "reduc_i32_cost": "int",
> > + "reduc_i64_cost": "int",
> > + "reduc_f16_cost": "int",
> > + "reduc_f32_cost": "int",
> > + "reduc_f64_cost": "int",
> > + "store_elt_extra_cost": "int",
> > + "vec_to_scalar_cost": "int",
> > + "scalar_to_vec_cost": "int",
> > + "align_load_cost": "int",
> > + "unalign_load_cost": "int",
> > + "unalign_store_cost": "int",
> > + "store_cost": "int"
> > + },
> > + "sve": {
> > + "clast_cost": "int",
> > + "fadda_f16_cost": "int",
> > + "fadda_f32_cost": "int",
> > + "fadda_f64_cost": "int",
> > + "gather_load_x32_cost": "uint",
> > + "gather_load_x64_cost": "uint",
> > + "gather_load_x32_init_cost": "int",
> > + "gather_load_x64_init_cost": "int",
> > + "scatter_store_elt_cost": "int"
> > + },
> > + "issue_info": {
> > + "scalar": {
> > + "loads_stores_per_cycle": "uint",
> > + "stores_per_cycle": "uint",
> > + "general_ops_per_cycle": "uint",
> > + "fp_simd_load_general_ops": "uint",
> > + "fp_simd_store_general_ops": "uint"
> > + },
> > + "advsimd": {
> > + "loads_stores_per_cycle": "uint",
> > + "stores_per_cycle": "uint",
> > + "general_ops_per_cycle": "uint",
> > + "fp_simd_load_general_ops": "uint",
> > + "fp_simd_store_general_ops": "uint",
> > + "ld2_st2_general_ops": "uint",
> > + "ld3_st3_general_ops": "uint",
> > + "ld4_st4_general_ops": "uint"
> > + },
> > + "sve": {
> > + "loads_stores_per_cycle": "uint",
> > + "stores_per_cycle": "uint",
> > + "general_ops_per_cycle": "uint",
> > + "fp_simd_load_general_ops": "uint",
> > + "fp_simd_store_general_ops": "uint",
> > + "ld2_st2_general_ops": "uint",
> > + "ld3_st3_general_ops": "uint",
> > + "ld4_st4_general_ops": "uint",
> > + "pred_ops_per_cycle": "uint",
> > + "while_pred_ops": "uint",
> > + "int_cmp_pred_ops": "uint",
> > + "fp_cmp_pred_ops": "uint",
> > + "gather_scatter_pair_general_ops": "uint",
> > + "gather_scatter_pair_pred_ops": "uint"
> > + }
> > + }
> > + },
> > + "branch_costs": { "predictable": "int", "unpredictable": "int" },
> > + "approx_modes": { "division": "int", "sqrt": "int", "recip_sqrt":
> > "int" },
> > + "sve_width": "uint",
> > + "memmov_cost": {
> > + "load_int": "int",
> > + "store_int": "int",
> > + "load_fp": "int",
> > + "store_fp": "int",
> > + "load_pred": "int",
> > + "store_pred": "int"
> > + },
> > + "issue_rate": "int",
> > + "fusible_ops": "uint",
> > + "function_align": "string",
> > + "jump_align": "string",
> > + "loop_align": "string",
> > + "int_reassoc_width": "int",
> > + "fp_reassoc_width": "int",
> > + "fma_reassoc_width": "int",
> > + "vec_reassoc_width": "int",
> > + "min_div_recip_mul_sf": "int",
> > + "min_div_recip_mul_df": "int",
> > + "max_case_values": "uint",
> > + "autoprefetcher_model": "enum",
> > + "extra_tuning_flags": "uint",
> > + "prefetch": {
> > + "num_slots": "int",
> > + "l1_cache_size": "int",
> > + "l1_cache_line_size": "int",
> > + "l2_cache_size": "int",
> > + "prefetch_dynamic_strides": "boolean",
> > + "minimum_stride": "int",
> > + "default_opt_level": "int"
> > + },
> > + "ldp_policy_model": "enum",
> > + "stp_policy_model": "enum"
> > + }
> > +})json";
> > +
> > +#endif
> > \ No newline at end of file
> > diff --git a/gcc/config/aarch64/aarch64-json-tunings-parser.cc
> > b/gcc/config/aarch64/aarch64-json-tunings-parser.cc
> > new file mode 100644
> > index 00000000000..23e742d437f
> > --- /dev/null
> > +++ b/gcc/config/aarch64/aarch64-json-tunings-parser.cc
> > @@ -0,0 +1,902 @@
> > +/* Routines to parse the AArch64 tuning parameters from a JSON file.
> > + Copyright The GNU Toolchain Authors.
> > +
> > + 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/>. */
> > +
> > +#define INCLUDE_STRING
> > +#define INCLUDE_VECTOR
> > +#define INCLUDE_TYPE_TRAITS
> > +#include "config.h"
> > +#include "system.h"
> > +#include "coretypes.h"
> > +#include "tm.h"
> > +#include "diagnostic-core.h"
> > +#include "json-parsing.h"
> > +#include "aarch64-json-schema.h"
> > +#include "aarch64-json-tunings-parser.h"
> > +#include "aarch64-protos.h"
> > +#include "config/arm/aarch-common-protos.h"
> > +#include "selftest.h"
> > +
> > +#define PARSE_INTEGER_FIELD(obj, key, member)
> > \
> > + {
> > \
> > + const json::value *val = obj->get (key);
> > \
> > + if (val)
> > \
> > + member = extract_integer (val);
> > \
> > + }
> > +
> > +#define PARSE_BOOLEAN_FIELD(obj, key, member)
> > \
> > + {
> > \
> > + const json::value *val = obj->get (key);
> > \
> > + if (val)
> > \
> > + member = extract_boolean (val);
> > \
> > + }
> > +
> > +#define PARSE_STRING_FIELD(obj, key, member)
> > \
> > + {
> > \
> > + const json::value *val = obj->get (key);
> > \
> > + if (val)
> > \
> > + member = extract_string (val);
> > \
> > + }
> > +
> > +#define PARSE_OBJECT(obj, key, member, parse_func)
> > \
> > + {
> > \
> > + const json::value *field_value = obj->get (key);
> > \
> > + if (field_value)
> > \
> > + if (auto *field_obj = dyn_cast<const json::object *> (field_value))
> > \
> > + parse_object_helper (field_obj, (member), (parse_func)); \
> > + }
> > +
> > +#define PARSE_ARRAY_FIELD(obj, key, member, parse_func)
> > \
> > + {
> > \
> > + const json::value *field_value = obj->get (key);
> > \
> > + if (field_value)
> > \
> > + if (auto *field_array = dyn_cast<const json::array *> (field_value))
> > \
> > + for (size_t i = 0; i < field_array->size (); ++i) \
> > + { \
> > + const json::value *elem = field_array->get (i); \
> > + if (elem) \
> > + if (auto *array_obj = dyn_cast<const json::object *> (elem)) \
> > + parse_func (array_obj, member[i]); \
> > + } \
> > + }
> > +
> > +#define PARSE_ENUM_FIELD(obj, key, member, mappings)
> > \
> > + parse_enum_field (obj, key, member, mappings,
> > \
> > + sizeof (mappings) / sizeof (mappings[0]))
> > +
> > +/* Type alias for parse function pointer. */
> > +template <typename T>
> > +using parse_func_type
> > + = void (*) (const json::object *,
> > + std::remove_const_t<std::remove_pointer_t<T>> &);
> > +
> > +/* Parse JSON object into non-pointer member type. */
> > +template <typename T>
> > +static std::enable_if_t<!std::is_pointer<T>::value>
> > +parse_object_helper (const json::object *field_obj, T &member,
> > + parse_func_type<T> parse_func)
> > +{
> > + parse_func (field_obj, member);
> > +}
> > +
> > +/* Parse JSON object into a const pointer member by creating a temp copy.
> > */
> > +template <typename T>
> > +static std::enable_if_t<std::is_pointer<T>::value
> > + && std::is_const<std::remove_pointer_t<T>>::value>
> > +parse_object_helper (const json::object *field_obj, T &member,
> > + parse_func_type<T> parse_func)
> > +{
> > + if (!member)
> > + return;
> > +
> > + /* Use static storage for the non-const copy.
> > + This works because tune_params does not have nested structures of the
> > + same type, but has room for errors if we end up having pointers to the
> > + same structure at some point. */
> > + static bool already_initialized = false;
> > + if (already_initialized)
> > + {
> > + error ("static storage conflict - multiple pointer members of the "
> > + "same type cannot be parsed");
> > + return;
> > + }
> > + already_initialized = true;
> > + using NonConstType = std::remove_const_t<std::remove_pointer_t<T>>;
> > + static NonConstType new_obj = *member;
> > + parse_func (field_obj, new_obj);
> > + member = &new_obj;
> > +}
> > +
> > +/* Extract string value from JSON, returning allocated C string. */
> > +char *
> > +extract_string (const json::value *val)
> > +{
> > + if (auto *string_val = dyn_cast<const json::string *> (val))
> > + return xstrdup (string_val->get_string ());
> > + warning (0, "expected a string but got something else or NULL");
> > + return nullptr;
> > +}
> > +
> > +/* Extract integer value from JSON. */
> > +int
> > +extract_integer (const json::value *val)
> > +{
> > + if (auto *int_val = dyn_cast<const json::integer_number *> (val))
> > + {
> > + long value = int_val->get ();
> > + if (value > INT_MAX)
> > + {
> > + error ("value exceeds %<INT_MAX%>");
> > + return 0;
> > + }
> > + return value;
> > + }
> > + warning (0, "expected an integer value but got something else or NULL");
> > + return 0;
> > +}
> > +
> > +/* Extract boolean value from JSON literal. */
> > +bool
> > +extract_boolean (const json::value *val)
> > +{
> > + if (auto *literal_val = dyn_cast<const json::literal *> (val))
> > + {
> > + json::kind kind = literal_val->get_kind ();
> > + if (kind == json::JSON_TRUE || kind == json::JSON_FALSE)
> > + return (kind == json::JSON_TRUE);
> > + }
> > + warning (0, "expected a boolean value but got something else or NULL");
> > + return false;
> > +}
> > +
> > +template <typename EnumType> struct enum_mapping
> > +{
> > + const char *name;
> > + EnumType value;
> > +};
> > +
> > +/* Parse JSON string field into enum value using string-to-enum mappings.
> > */
> > +template <typename EnumType>
> > +static void
> > +parse_enum_field (const json::object *jo, const std::string &key,
> > + EnumType &enum_var, const enum_mapping<EnumType>
> > *mappings,
> > + size_t num_mappings)
> > +{
> > + const json::value *field_value = jo->get (key.c_str ());
> > + if (!field_value)
> > + return;
> > +
> > + auto *string_val = dyn_cast<const json::string *> (field_value);
> > + if (!string_val)
> > + {
> > + warning (0, "expected string for enum field %s", key.c_str ());
> > + enum_var = mappings[0].value;
> > + return;
> > + }
> > +
> > + const char *field_string = string_val->get_string ();
> > + for (size_t i = 0; i < num_mappings; ++i)
> > + {
> > + if (strcmp (field_string, mappings[i].name) == 0)
> > + {
> > + enum_var = mappings[i].value;
> > + return;
> > + }
> > + }
> > +
> > + warning (0, "%s not recognized, defaulting to %qs", key.c_str (),
> > + mappings[0].name);
> > + enum_var = mappings[0].value;
> > +}
> > +
> > +/* Enum mappings for known tuning parameter enums. */
> > +static const enum_mapping<tune_params::aarch64_autoprefetch_model>
> > + autoprefetcher_model_mappings[]
> > + = {{"AUTOPREFETCHER_OFF", tune_params::AUTOPREFETCHER_OFF},
> > + {"AUTOPREFETCHER_WEAK", tune_params::AUTOPREFETCHER_WEAK},
> > + {"AUTOPREFETCHER_STRONG",
> > tune_params::AUTOPREFETCHER_STRONG}};
> > +
> > +static const enum_mapping<aarch64_ldp_stp_policy>
> > ldp_policy_model_mappings[]
> > + = {{"AARCH64_LDP_STP_POLICY_DEFAULT",
> > AARCH64_LDP_STP_POLICY_DEFAULT},
> > + {"AARCH64_LDP_STP_POLICY_ALIGNED",
> > AARCH64_LDP_STP_POLICY_ALIGNED},
> > + {"AARCH64_LDP_STP_POLICY_ALWAYS",
> > AARCH64_LDP_STP_POLICY_ALWAYS},
> > + {"AARCH64_LDP_STP_POLICY_NEVER",
> > AARCH64_LDP_STP_POLICY_NEVER}};
> > +
> > +static const enum_mapping<aarch64_ldp_stp_policy>
> > stp_policy_model_mappings[]
> > + = {{"AARCH64_LDP_STP_POLICY_DEFAULT",
> > AARCH64_LDP_STP_POLICY_DEFAULT},
> > + {"AARCH64_LDP_STP_POLICY_ALIGNED",
> > AARCH64_LDP_STP_POLICY_ALIGNED},
> > + {"AARCH64_LDP_STP_POLICY_ALWAYS",
> > AARCH64_LDP_STP_POLICY_ALWAYS},
> > + {"AARCH64_LDP_STP_POLICY_NEVER",
> > AARCH64_LDP_STP_POLICY_NEVER}};
> > +
> > +template <typename T>
> > +static void
> > +parse_insn_extra_cost_alu (const json::object *jo, T &alu)
> > +{
> > + PARSE_INTEGER_FIELD (jo, "arith", alu.arith);
> > + PARSE_INTEGER_FIELD (jo, "logical", alu.logical);
> > + PARSE_INTEGER_FIELD (jo, "shift", alu.shift);
> > + PARSE_INTEGER_FIELD (jo, "shift_reg", alu.shift_reg);
> > + PARSE_INTEGER_FIELD (jo, "arith_shift", alu.arith_shift);
> > + PARSE_INTEGER_FIELD (jo, "arith_shift_reg", alu.arith_shift_reg);
> > + PARSE_INTEGER_FIELD (jo, "log_shift", alu.log_shift);
> > + PARSE_INTEGER_FIELD (jo, "log_shift_reg", alu.log_shift_reg);
> > + PARSE_INTEGER_FIELD (jo, "extend", alu.extend);
> > + PARSE_INTEGER_FIELD (jo, "extend_arith", alu.extend_arith);
> > + PARSE_INTEGER_FIELD (jo, "bfi", alu.bfi);
> > + PARSE_INTEGER_FIELD (jo, "bfx", alu.bfx);
> > + PARSE_INTEGER_FIELD (jo, "clz", alu.clz);
> > + PARSE_INTEGER_FIELD (jo, "rev", alu.rev);
> > + PARSE_INTEGER_FIELD (jo, "non_exec", alu.non_exec);
> > + PARSE_BOOLEAN_FIELD (jo, "non_exec_costs_exec",
> > alu.non_exec_costs_exec);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_insn_extra_cost_mult_element (const json::object *jo, T
> > &mult_element)
> > +{
> > + PARSE_INTEGER_FIELD (jo, "simple", mult_element.simple);
> > + PARSE_INTEGER_FIELD (jo, "flag_setting", mult_element.flag_setting);
> > + PARSE_INTEGER_FIELD (jo, "extend", mult_element.extend);
> > + PARSE_INTEGER_FIELD (jo, "add", mult_element.add);
> > + PARSE_INTEGER_FIELD (jo, "extend_add", mult_element.extend_add);
> > + PARSE_INTEGER_FIELD (jo, "idiv", mult_element.idiv);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_insn_extra_cost_ldst (const json::object *jo, T &ldst)
> > +{
> > + PARSE_INTEGER_FIELD (jo, "load", ldst.load);
> > + PARSE_INTEGER_FIELD (jo, "load_sign_extend", ldst.load_sign_extend);
> > + PARSE_INTEGER_FIELD (jo, "ldrd", ldst.ldrd);
> > + PARSE_INTEGER_FIELD (jo, "ldm_1st", ldst.ldm_1st);
> > + PARSE_INTEGER_FIELD (jo, "ldm_regs_per_insn_1st",
> > ldst.ldm_regs_per_insn_1st);
> > + PARSE_INTEGER_FIELD (jo, "ldm_regs_per_insn_subsequent",
> > + ldst.ldm_regs_per_insn_subsequent);
> > + PARSE_INTEGER_FIELD (jo, "loadf", ldst.loadf);
> > + PARSE_INTEGER_FIELD (jo, "loadd", ldst.loadd);
> > + PARSE_INTEGER_FIELD (jo, "load_unaligned", ldst.load_unaligned);
> > + PARSE_INTEGER_FIELD (jo, "store", ldst.store);
> > + PARSE_INTEGER_FIELD (jo, "strd", ldst.strd);
> > + PARSE_INTEGER_FIELD (jo, "stm_1st", ldst.stm_1st);
> > + PARSE_INTEGER_FIELD (jo, "stm_regs_per_insn_1st",
> > ldst.stm_regs_per_insn_1st);
> > + PARSE_INTEGER_FIELD (jo, "stm_regs_per_insn_subsequent",
> > + ldst.stm_regs_per_insn_subsequent);
> > + PARSE_INTEGER_FIELD (jo, "storef", ldst.storef);
> > + PARSE_INTEGER_FIELD (jo, "stored", ldst.stored);
> > + PARSE_INTEGER_FIELD (jo, "store_unaligned", ldst.store_unaligned);
> > + PARSE_INTEGER_FIELD (jo, "loadv", ldst.loadv);
> > + PARSE_INTEGER_FIELD (jo, "storev", ldst.storev);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_insn_extra_cost_fp_element (const json::object *jo, T &fp_element)
> > +{
> > + PARSE_INTEGER_FIELD (jo, "div", fp_element.div);
> > + PARSE_INTEGER_FIELD (jo, "mult", fp_element.mult);
> > + PARSE_INTEGER_FIELD (jo, "mult_addsub", fp_element.mult_addsub);
> > + PARSE_INTEGER_FIELD (jo, "fma", fp_element.fma);
> > + PARSE_INTEGER_FIELD (jo, "addsub", fp_element.addsub);
> > + PARSE_INTEGER_FIELD (jo, "fpconst", fp_element.fpconst);
> > + PARSE_INTEGER_FIELD (jo, "neg", fp_element.neg);
> > + PARSE_INTEGER_FIELD (jo, "compare", fp_element.compare);
> > + PARSE_INTEGER_FIELD (jo, "widen", fp_element.widen);
> > + PARSE_INTEGER_FIELD (jo, "narrow", fp_element.narrow);
> > + PARSE_INTEGER_FIELD (jo, "toint", fp_element.toint);
> > + PARSE_INTEGER_FIELD (jo, "fromint", fp_element.fromint);
> > + PARSE_INTEGER_FIELD (jo, "roundint", fp_element.roundint);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_insn_extra_cost_vect (const json::object *jo, T &vect)
> > +{
> > + PARSE_INTEGER_FIELD (jo, "alu", vect.alu);
> > + PARSE_INTEGER_FIELD (jo, "mult", vect.mult);
> > + PARSE_INTEGER_FIELD (jo, "movi", vect.movi);
> > + PARSE_INTEGER_FIELD (jo, "dup", vect.dup);
> > + PARSE_INTEGER_FIELD (jo, "extract", vect.extract);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_addr_cost_addr_scale_costs (const json::object *jo, T
> > &addr_scale_costs)
> > +{
> > + PARSE_INTEGER_FIELD (jo, "hi", addr_scale_costs.hi);
> > + PARSE_INTEGER_FIELD (jo, "si", addr_scale_costs.si);
> > + PARSE_INTEGER_FIELD (jo, "di", addr_scale_costs.di);
> > + PARSE_INTEGER_FIELD (jo, "ti", addr_scale_costs.ti);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_regmove_cost (const json::object *jo, T ®move_cost)
> > +{
> > + PARSE_INTEGER_FIELD (jo, "GP2GP", regmove_cost.GP2GP);
> > + PARSE_INTEGER_FIELD (jo, "GP2FP", regmove_cost.GP2FP);
> > + PARSE_INTEGER_FIELD (jo, "FP2GP", regmove_cost.FP2GP);
> > + PARSE_INTEGER_FIELD (jo, "FP2FP", regmove_cost.FP2FP);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_vec_costs_advsimd (const json::object *jo, T &advsimd)
> > +{
> > + PARSE_INTEGER_FIELD (jo, "int_stmt_cost", advsimd.int_stmt_cost);
> > + PARSE_INTEGER_FIELD (jo, "fp_stmt_cost", advsimd.fp_stmt_cost);
> > + PARSE_INTEGER_FIELD (jo, "ld2_st2_permute_cost",
> > + advsimd.ld2_st2_permute_cost);
> > + PARSE_INTEGER_FIELD (jo, "ld3_st3_permute_cost",
> > + advsimd.ld3_st3_permute_cost);
> > + PARSE_INTEGER_FIELD (jo, "ld4_st4_permute_cost",
> > + advsimd.ld4_st4_permute_cost);
> > + PARSE_INTEGER_FIELD (jo, "permute_cost", advsimd.permute_cost);
> > + PARSE_INTEGER_FIELD (jo, "reduc_i8_cost", advsimd.reduc_i8_cost);
> > + PARSE_INTEGER_FIELD (jo, "reduc_i16_cost", advsimd.reduc_i16_cost);
> > + PARSE_INTEGER_FIELD (jo, "reduc_i32_cost", advsimd.reduc_i32_cost);
> > + PARSE_INTEGER_FIELD (jo, "reduc_i64_cost", advsimd.reduc_i64_cost);
> > + PARSE_INTEGER_FIELD (jo, "reduc_f16_cost", advsimd.reduc_f16_cost);
> > + PARSE_INTEGER_FIELD (jo, "reduc_f32_cost", advsimd.reduc_f32_cost);
> > + PARSE_INTEGER_FIELD (jo, "reduc_f64_cost", advsimd.reduc_f64_cost);
> > + PARSE_INTEGER_FIELD (jo, "store_elt_extra_cost",
> > + advsimd.store_elt_extra_cost);
> > + PARSE_INTEGER_FIELD (jo, "vec_to_scalar_cost",
> > advsimd.vec_to_scalar_cost);
> > + PARSE_INTEGER_FIELD (jo, "scalar_to_vec_cost",
> > advsimd.scalar_to_vec_cost);
> > + PARSE_INTEGER_FIELD (jo, "align_load_cost", advsimd.align_load_cost);
> > + PARSE_INTEGER_FIELD (jo, "unalign_load_cost",
> > advsimd.unalign_load_cost);
> > + PARSE_INTEGER_FIELD (jo, "unalign_store_cost",
> > advsimd.unalign_store_cost);
> > + PARSE_INTEGER_FIELD (jo, "store_cost", advsimd.store_cost);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_vec_costs_sve (const json::object *jo, T &sve)
> > +{
> > + PARSE_INTEGER_FIELD (jo, "clast_cost", sve.clast_cost);
> > + PARSE_INTEGER_FIELD (jo, "fadda_f16_cost", sve.fadda_f16_cost);
> > + PARSE_INTEGER_FIELD (jo, "fadda_f32_cost", sve.fadda_f32_cost);
> > + PARSE_INTEGER_FIELD (jo, "fadda_f64_cost", sve.fadda_f64_cost);
> > + PARSE_INTEGER_FIELD (jo, "gather_load_x32_cost",
> > sve.gather_load_x32_cost);
> > + PARSE_INTEGER_FIELD (jo, "gather_load_x64_cost",
> > sve.gather_load_x64_cost);
> > + PARSE_INTEGER_FIELD (jo, "gather_load_x32_init_cost",
> > + sve.gather_load_x32_init_cost);
> > + PARSE_INTEGER_FIELD (jo, "gather_load_x64_init_cost",
> > + sve.gather_load_x64_init_cost);
> > + PARSE_INTEGER_FIELD (jo, "scatter_store_elt_cost",
> > + sve.scatter_store_elt_cost);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_vec_costs_issue_info_scalar (const json::object *jo, T &scalar)
> > +{
> > + PARSE_INTEGER_FIELD (jo, "loads_stores_per_cycle",
> > + scalar.loads_stores_per_cycle);
> > + PARSE_INTEGER_FIELD (jo, "stores_per_cycle", scalar.stores_per_cycle);
> > + PARSE_INTEGER_FIELD (jo, "general_ops_per_cycle",
> > + scalar.general_ops_per_cycle);
> > + PARSE_INTEGER_FIELD (jo, "fp_simd_load_general_ops",
> > + scalar.fp_simd_load_general_ops);
> > + PARSE_INTEGER_FIELD (jo, "fp_simd_store_general_ops",
> > + scalar.fp_simd_store_general_ops);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_vec_costs_issue_info_advsimd (const json::object *jo, T &advsimd)
> > +{
> > + PARSE_INTEGER_FIELD (jo, "loads_stores_per_cycle",
> > + advsimd.loads_stores_per_cycle);
> > + PARSE_INTEGER_FIELD (jo, "stores_per_cycle", advsimd.stores_per_cycle);
> > + PARSE_INTEGER_FIELD (jo, "general_ops_per_cycle",
> > + advsimd.general_ops_per_cycle);
> > + PARSE_INTEGER_FIELD (jo, "fp_simd_load_general_ops",
> > + advsimd.fp_simd_load_general_ops);
> > + PARSE_INTEGER_FIELD (jo, "fp_simd_store_general_ops",
> > + advsimd.fp_simd_store_general_ops);
> > + PARSE_INTEGER_FIELD (jo, "ld2_st2_general_ops",
> > advsimd.ld2_st2_general_ops);
> > + PARSE_INTEGER_FIELD (jo, "ld3_st3_general_ops",
> > advsimd.ld3_st3_general_ops);
> > + PARSE_INTEGER_FIELD (jo, "ld4_st4_general_ops",
> > advsimd.ld4_st4_general_ops);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_vec_costs_issue_info_sve (const json::object *jo, T &sve)
> > +{
> > + PARSE_INTEGER_FIELD (jo, "loads_stores_per_cycle",
> > + sve.loads_stores_per_cycle);
> > + PARSE_INTEGER_FIELD (jo, "stores_per_cycle", sve.stores_per_cycle);
> > + PARSE_INTEGER_FIELD (jo, "general_ops_per_cycle",
> > sve.general_ops_per_cycle);
> > + PARSE_INTEGER_FIELD (jo, "fp_simd_load_general_ops",
> > + sve.fp_simd_load_general_ops);
> > + PARSE_INTEGER_FIELD (jo, "fp_simd_store_general_ops",
> > + sve.fp_simd_store_general_ops);
> > + PARSE_INTEGER_FIELD (jo, "ld2_st2_general_ops",
> > sve.ld2_st2_general_ops);
> > + PARSE_INTEGER_FIELD (jo, "ld3_st3_general_ops",
> > sve.ld3_st3_general_ops);
> > + PARSE_INTEGER_FIELD (jo, "ld4_st4_general_ops",
> > sve.ld4_st4_general_ops);
> > + PARSE_INTEGER_FIELD (jo, "pred_ops_per_cycle",
> sve.pred_ops_per_cycle);
> > + PARSE_INTEGER_FIELD (jo, "while_pred_ops", sve.while_pred_ops);
> > + PARSE_INTEGER_FIELD (jo, "int_cmp_pred_ops", sve.int_cmp_pred_ops);
> > + PARSE_INTEGER_FIELD (jo, "fp_cmp_pred_ops", sve.fp_cmp_pred_ops);
> > + PARSE_INTEGER_FIELD (jo, "gather_scatter_pair_general_ops",
> > + sve.gather_scatter_pair_general_ops);
> > + PARSE_INTEGER_FIELD (jo, "gather_scatter_pair_pred_ops",
> > + sve.gather_scatter_pair_pred_ops);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_branch_costs (const json::object *jo, T &branch_costs)
> > +{
> > + PARSE_INTEGER_FIELD (jo, "predictable", branch_costs.predictable);
> > + PARSE_INTEGER_FIELD (jo, "unpredictable", branch_costs.unpredictable);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_approx_modes (const json::object *jo, T &approx_modes)
> > +{
> > + PARSE_INTEGER_FIELD (jo, "division", approx_modes.division);
> > + PARSE_INTEGER_FIELD (jo, "sqrt", approx_modes.sqrt);
> > + PARSE_INTEGER_FIELD (jo, "recip_sqrt", approx_modes.recip_sqrt);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_memmov_cost (const json::object *jo, T &memmov_cost)
> > +{
> > + PARSE_INTEGER_FIELD (jo, "load_int", memmov_cost.load_int);
> > + PARSE_INTEGER_FIELD (jo, "store_int", memmov_cost.store_int);
> > + PARSE_INTEGER_FIELD (jo, "load_fp", memmov_cost.load_fp);
> > + PARSE_INTEGER_FIELD (jo, "store_fp", memmov_cost.store_fp);
> > + PARSE_INTEGER_FIELD (jo, "load_pred", memmov_cost.load_pred);
> > + PARSE_INTEGER_FIELD (jo, "store_pred", memmov_cost.store_pred);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_prefetch (const json::object *jo, T &prefetch)
> > +{
> > + PARSE_INTEGER_FIELD (jo, "num_slots", prefetch.num_slots);
> > + PARSE_INTEGER_FIELD (jo, "l1_cache_size", prefetch.l1_cache_size);
> > + PARSE_INTEGER_FIELD (jo, "l1_cache_line_size",
> > prefetch.l1_cache_line_size);
> > + PARSE_INTEGER_FIELD (jo, "l2_cache_size", prefetch.l2_cache_size);
> > + PARSE_BOOLEAN_FIELD (jo, "prefetch_dynamic_strides",
> > + prefetch.prefetch_dynamic_strides);
> > + PARSE_INTEGER_FIELD (jo, "minimum_stride", prefetch.minimum_stride);
> > + PARSE_INTEGER_FIELD (jo, "default_opt_level",
> prefetch.default_opt_level);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_insn_extra_cost (const json::object *jo, T &insn_extra_cost)
> > +{
> > + PARSE_OBJECT (jo, "alu", insn_extra_cost.alu, parse_insn_extra_cost_alu);
> > + PARSE_ARRAY_FIELD (jo, "mult", insn_extra_cost.mult,
> > + parse_insn_extra_cost_mult_element);
> > + PARSE_OBJECT (jo, "ldst", insn_extra_cost.ldst,
> > parse_insn_extra_cost_ldst);
> > + PARSE_ARRAY_FIELD (jo, "fp", insn_extra_cost.fp,
> > + parse_insn_extra_cost_fp_element);
> > + PARSE_OBJECT (jo, "vect", insn_extra_cost.vect,
> > parse_insn_extra_cost_vect);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_addr_cost (const json::object *jo, T &addr_cost)
> > +{
> > + PARSE_OBJECT (jo, "addr_scale_costs", addr_cost.addr_scale_costs,
> > + parse_addr_cost_addr_scale_costs);
> > + PARSE_INTEGER_FIELD (jo, "pre_modify", addr_cost.pre_modify);
> > + PARSE_INTEGER_FIELD (jo, "post_modify", addr_cost.post_modify);
> > + PARSE_INTEGER_FIELD (jo, "post_modify_ld3_st3",
> > + addr_cost.post_modify_ld3_st3);
> > + PARSE_INTEGER_FIELD (jo, "post_modify_ld4_st4",
> > + addr_cost.post_modify_ld4_st4);
> > + PARSE_INTEGER_FIELD (jo, "register_offset", addr_cost.register_offset);
> > + PARSE_INTEGER_FIELD (jo, "register_sextend",
> addr_cost.register_sextend);
> > + PARSE_INTEGER_FIELD (jo, "register_zextend",
> addr_cost.register_zextend);
> > + PARSE_INTEGER_FIELD (jo, "imm_offset", addr_cost.imm_offset);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_vec_costs_issue_info (const json::object *jo, T &issue_info)
> > +{
> > + PARSE_OBJECT (jo, "scalar", issue_info.scalar,
> > + parse_vec_costs_issue_info_scalar);
> > + PARSE_OBJECT (jo, "advsimd", issue_info.advsimd,
> > + parse_vec_costs_issue_info_advsimd);
> > + PARSE_OBJECT (jo, "sve", issue_info.sve,
> parse_vec_costs_issue_info_sve);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_vec_costs (const json::object *jo, T &vec_costs)
> > +{
> > + PARSE_INTEGER_FIELD (jo, "scalar_int_stmt_cost",
> > + vec_costs.scalar_int_stmt_cost);
> > + PARSE_INTEGER_FIELD (jo, "scalar_fp_stmt_cost",
> > + vec_costs.scalar_fp_stmt_cost);
> > + PARSE_INTEGER_FIELD (jo, "scalar_load_cost",
> vec_costs.scalar_load_cost);
> > + PARSE_INTEGER_FIELD (jo, "scalar_store_cost",
> > vec_costs.scalar_store_cost);
> > + PARSE_INTEGER_FIELD (jo, "cond_taken_branch_cost",
> > + vec_costs.cond_taken_branch_cost);
> > + PARSE_INTEGER_FIELD (jo, "cond_not_taken_branch_cost",
> > + vec_costs.cond_not_taken_branch_cost);
> > + PARSE_OBJECT (jo, "advsimd", vec_costs.advsimd,
> > parse_vec_costs_advsimd);
> > + PARSE_OBJECT (jo, "sve", vec_costs.sve, parse_vec_costs_sve);
> > + PARSE_OBJECT (jo, "issue_info", vec_costs.issue_info,
> > + parse_vec_costs_issue_info);
> > +}
> > +
> > +template <typename T>
> > +static void
> > +parse_tunings (const json::object *jo, T &tunings)
> > +{
> > + PARSE_OBJECT (jo, "insn_extra_cost", tunings.insn_extra_cost,
> > + parse_insn_extra_cost);
> > + PARSE_OBJECT (jo, "addr_cost", tunings.addr_cost, parse_addr_cost);
> > + PARSE_OBJECT (jo, "regmove_cost", tunings.regmove_cost,
> > parse_regmove_cost);
> > + PARSE_OBJECT (jo, "vec_costs", tunings.vec_costs, parse_vec_costs);
> > + PARSE_OBJECT (jo, "branch_costs", tunings.branch_costs,
> > parse_branch_costs);
> > + PARSE_OBJECT (jo, "approx_modes", tunings.approx_modes,
> > parse_approx_modes);
> > + PARSE_INTEGER_FIELD (jo, "sve_width", tunings.sve_width);
> > + PARSE_OBJECT (jo, "memmov_cost", tunings.memmov_cost,
> > parse_memmov_cost);
> > + PARSE_INTEGER_FIELD (jo, "issue_rate", tunings.issue_rate);
> > + PARSE_INTEGER_FIELD (jo, "fusible_ops", tunings.fusible_ops);
> > + PARSE_STRING_FIELD (jo, "function_align", tunings.function_align);
> > + PARSE_STRING_FIELD (jo, "jump_align", tunings.jump_align);
> > + PARSE_STRING_FIELD (jo, "loop_align", tunings.loop_align);
> > + PARSE_INTEGER_FIELD (jo, "int_reassoc_width",
> > tunings.int_reassoc_width);
> > + PARSE_INTEGER_FIELD (jo, "fp_reassoc_width",
> tunings.fp_reassoc_width);
> > + PARSE_INTEGER_FIELD (jo, "fma_reassoc_width",
> > tunings.fma_reassoc_width);
> > + PARSE_INTEGER_FIELD (jo, "vec_reassoc_width",
> > tunings.vec_reassoc_width);
> > + PARSE_INTEGER_FIELD (jo, "min_div_recip_mul_sf",
> > + tunings.min_div_recip_mul_sf);
> > + PARSE_INTEGER_FIELD (jo, "min_div_recip_mul_df",
> > + tunings.min_div_recip_mul_df);
> > + PARSE_INTEGER_FIELD (jo, "max_case_values", tunings.max_case_values);
> > + PARSE_ENUM_FIELD (jo, "autoprefetcher_model",
> > tunings.autoprefetcher_model,
> > + autoprefetcher_model_mappings);
> > + PARSE_INTEGER_FIELD (jo, "extra_tuning_flags",
> > tunings.extra_tuning_flags);
> > + PARSE_OBJECT (jo, "prefetch", tunings.prefetch, parse_prefetch);
> > + PARSE_ENUM_FIELD (jo, "ldp_policy_model", tunings.ldp_policy_model,
> > + ldp_policy_model_mappings);
> > + PARSE_ENUM_FIELD (jo, "stp_policy_model", tunings.stp_policy_model,
> > + stp_policy_model_mappings);
> > +}
> > +
> > +/* Validate the user provided JSON data against the present schema.
> > + Checks for correct types, fields, and expected format. */
> > +static bool
> > +validate_and_traverse (const json::object *json_obj,
> > + const json::object *schema_obj,
> > + const std::string &parent_key = "")
> > +{
> > + for (const auto &json_entry : json_obj->get_map ())
> > + {
> > + const std::string &key = json_entry.first;
> > + const json::value *json_value = json_entry.second;
> > +
> > + std::string full_key = parent_key.empty () ? key : parent_key + "."
> > + key;
> > +
> > + const json::value *schema_value = schema_obj->get (key.c_str ());
> > + if (!schema_value)
> > + {
> > + warning (0, "key %qs is not a tuning parameter, skipping",
> > + full_key.c_str ());
> > + continue;
> > + }
> > +
> > + if (auto *sub_schema_obj = dyn_cast<const json::object *>
> > (schema_value))
> > + {
> > + if (auto *sub_json_obj = dyn_cast<const json::object *>
> > (json_value))
> > + {
> > + if (!validate_and_traverse (sub_json_obj, sub_schema_obj,
> > + full_key))
> > + return false;
> > + }
> > + else
> > + {
> > + error ("key %qs expected to be an object", full_key.c_str ());
> > + return false;
> > + }
> > + }
> > + else if (schema_value->get_kind () == json::JSON_ARRAY)
> > + {
> > + if (json_value->get_kind () != json::JSON_ARRAY)
> > + {
> > + error ("key %qs expected to be an array", full_key.c_str ());
> > + return false;
> > + }
> > + }
> > + else if (auto *schema_string
> > + = dyn_cast<const json::string *> (schema_value))
> > + {
> > + const char *schema_type_str = schema_string->get_string ();
> > +
> > + if (strcmp (schema_type_str, "int") == 0)
> > + {
> > + if (json_value->get_kind () != json::JSON_INTEGER)
> > + {
> > + error ("key %qs expected to be an integer",
> > + full_key.c_str ());
> > + return false;
> > + }
> > + }
> > + else if (strcmp (schema_type_str, "uint") == 0)
> > + {
> > + if (json_value->get_kind () != json::JSON_INTEGER
> > + || extract_integer (json_value) < 0)
> > + {
> > + error ("key %qs expected to be an unsigned integer",
> > + full_key.c_str ());
> > + return false;
> > + }
> > + }
> > + else if (strcmp (schema_type_str, "string") == 0)
> > + {
> > + if (json_value->get_kind () != json::JSON_STRING)
> > + {
> > + error ("key %qs expected to be a string", full_key.c_str ());
> > + return false;
> > + }
> > + }
> > + else if (strcmp (schema_type_str, "boolean") == 0)
> > + {
> > + if (json_value->get_kind () != json::JSON_TRUE
> > + && json_value->get_kind () != json::JSON_FALSE)
> > + {
> > + error ("key %qs expected to be a boolean (true/false)",
> > + full_key.c_str ());
> > + return false;
> > + }
> > + }
> > + else if (strcmp (schema_type_str, "enum") == 0)
> > + {
> > + if (json_value->get_kind () != json::JSON_STRING)
> > + {
> > + error ("key %qs expected to be an enum (string)",
> > + full_key.c_str ());
> > + return false;
> > + }
> > + }
> > + else
> > + {
> > + error ("key %qs has unsupported type", full_key.c_str ());
> > + return false;
> > + }
> > + }
> > + else
> > + {
> > + error ("key %qs has unexpected format in schema", full_key.c_str ());
> > + return false;
> > + }
> > + }
> > + return true;
> > +}
> > +
> > +/* Helper routine for reading the provided JSON file. */
> > +static std::unique_ptr<std::vector<char>>
> > +read_file (const char *path)
> > +{
> > + FILE *f_in = fopen (path, "r");
> > + if (!f_in)
> > + {
> > + error ("could not open file %s", path);
> > + return nullptr;
> > + }
> > +
> > + auto result = std::make_unique<std::vector<char>> ();
> > + char buf[4096];
> > +
> > + while (size_t iter_sz_in = fread (buf, 1, sizeof (buf), f_in))
> > + result->insert (result->end (), buf, buf + iter_sz_in);
> > +
> > + if (!feof (f_in))
> > + {
> > + error ("error reading file %s", path);
> > + fclose (f_in);
> > + return nullptr;
> > + }
> > +
> > + fclose (f_in);
> > + result->push_back ('\0');
> > + return result;
> > +}
> > +
> > +/* Main routine for setting up the parsing of JSON data. */
> > +void
> > +aarch64_load_tuning_params_from_json_string (const char *json_string,
> > + const char *schema_string,
> > + struct tune_params *tune)
> > +{
> > + /* Try parsing the JSON string. */
> > + json::parser_result_t data_result
> > + = json::parse_utf8_string (strlen (json_string), json_string, true,
> > + nullptr);
> > +
> > + if (auto json_err = data_result.m_err.get ())
> > + {
> > + error ("error parsing JSON data: %s", json_err->get_msg ());
> > + return;
> > + }
> > +
> > + const std::unique_ptr<json::value> &root = data_result.m_val;
> > + if (!root)
> > + {
> > + error ("JSON parsing returned null data");
> > + return;
> > + }
> > + auto *root_obj = dyn_cast<const json::object *> (root.get ());
> > + if (!root_obj)
> > + {
> > + warning (0, "no JSON object found in the provided data");
> > + return;
> > + }
> > +
> > + json::parser_result_t schema_result
> > + = json::parse_utf8_string (strlen (schema_string), schema_string, true,
> > + nullptr);
> > +
> > + gcc_assert (!schema_result.m_err.get ());
> > + gcc_assert (schema_result.m_val);
> > +
> > + auto *schema_obj
> > + = dyn_cast<const json::object *> (schema_result.m_val.get ());
> > + gcc_assert (schema_obj);
> > +
> > + const json::value *tune_params_value = root_obj->get ("tune_params");
> > + if (!tune_params_value)
> > + {
> > + warning (0, "key %<tune_params%> not found in JSON data");
> > + return;
> > + }
> > +
> > + auto *jo = dyn_cast<const json::object *> (tune_params_value);
> > + if (!jo)
> > + {
> > + error ("key %<tune_params%> is not a JSON object");
> > + return;
> > + }
> > +
> > + if (!validate_and_traverse (root_obj, schema_obj))
> > + {
> > + error ("validation failed for the provided JSON data");
> > + return;
> > + }
> > +
> > + parse_tunings (jo, *tune);
> > + return;
> > +}
> > +
> > +/* Wrapper for calling aarch64_load_tuning_params_from_json_string. */
> > +void
> > +aarch64_load_tuning_params_from_json (const char *data_filename,
> > + struct tune_params *tune)
> > +{
> > + std::unique_ptr<std::vector<char>> json_data = read_file (data_filename);
> > + if (!json_data || !json_data->data ())
> > + {
> > + error ("cannot read JSON data in %s", data_filename);
> > + return;
> > + }
> > + aarch64_load_tuning_params_from_json_string (
> > + (const char *) json_data->data (), schema_json, tune);
> > +}
> > +
> > +/* Self-tests to check if the values populated to the tune_params structure
> > + are correct. */
> > +
> > +#if CHECKING_P
> > +namespace selftest {
> > +
> > +void
> > +test_json_integers ()
> > +{
> > + const char *test_json = R"json({
> > + "tune_params": {
> > + "sve_width": 256,
> > + "issue_rate": 4
> > + }
> > + })json";
> > +
> > + tune_params params;
> > +
> > + aarch64_load_tuning_params_from_json_string (test_json, schema_json,
> > ¶ms);
> > +
> > + ASSERT_EQ (params.sve_width, 256);
> > + ASSERT_EQ (params.issue_rate, 4);
> > +}
> > +
> > +void
> > +test_json_boolean ()
> > +{
> > + const char *test_json = R"json({
> > + "tune_params": {
> > + "insn_extra_cost": {
> > + "alu": {
> > + "non_exec_costs_exec": false
> > + }
> > + }
> > + }
> > + })json";
> > +
> > + static const cpu_cost_table default_cost_table = {};
> > +
> > + tune_params params;
> > + params.insn_extra_cost = &default_cost_table;
> > +
> > + aarch64_load_tuning_params_from_json_string (test_json, schema_json,
> > ¶ms);
> > +
> > + ASSERT_EQ (params.insn_extra_cost->alu.non_exec_costs_exec, false);
> > +}
> > +
> > +void
> > +test_json_strings ()
> > +{
> > + const char *test_json = R"json({
> > + "tune_params": {
> > + "function_align": "16",
> > + "jump_align": "2",
> > + "loop_align": "8"
> > + }
> > + })json";
> > +
> > + tune_params params;
> > +
> > + aarch64_load_tuning_params_from_json_string (test_json, schema_json,
> > ¶ms);
> > +
> > + ASSERT_STREQ (params.function_align, "16");
> > + ASSERT_STREQ (params.jump_align, "2");
> > + ASSERT_STREQ (params.loop_align, "8");
> > +}
> > +
> > +void
> > +test_json_enums ()
> > +{
> > + const char *test_json = R"json({
> > + "tune_params": {
> > + "autoprefetcher_model": "AUTOPREFETCHER_OFF",
> > + "ldp_policy_model": "AARCH64_LDP_STP_POLICY_NEVER",
> > + "stp_policy_model": "AARCH64_LDP_STP_POLICY_DEFAULT"
> > + }
> > + })json";
> > +
> > + tune_params params;
> > +
> > + aarch64_load_tuning_params_from_json_string (test_json, schema_json,
> > ¶ms);
> > +
> > + ASSERT_EQ (params.autoprefetcher_model,
> > tune_params::AUTOPREFETCHER_OFF);
> > + ASSERT_EQ (params.ldp_policy_model,
> > AARCH64_LDP_STP_POLICY_NEVER);
> > + ASSERT_EQ (params.stp_policy_model,
> > AARCH64_LDP_STP_POLICY_DEFAULT);
> > +}
> > +
> > +void
> > +json_tunings_tests ()
> > +{
> > + test_json_integers ();
> > + test_json_boolean ();
> > + test_json_strings ();
> > + test_json_enums ();
> > +}
> > +
> > +} // namespace selftest
> > +
> > +#endif /* CHECKING_P */
> > \ No newline at end of file
> > diff --git a/gcc/config/aarch64/aarch64-json-tunings-parser.h
> > b/gcc/config/aarch64/aarch64-json-tunings-parser.h
> > new file mode 100644
> > index 00000000000..3c5cd4c3c20
> > --- /dev/null
> > +++ b/gcc/config/aarch64/aarch64-json-tunings-parser.h
> > @@ -0,0 +1,29 @@
> > +/* Routines to parse the AArch64 tuning parameters from a JSON file.
> > + Copyright The GNU Toolchain Authors.
> > +
> > + 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/>. */
> > +
> > +#ifndef AARCH64_JSON_TUNINGS_PARSER_H
> > +#define AARCH64_JSON_TUNINGS_PARSER_H
> > +
> > +#include "aarch64-protos.h"
> > +
> > +void
> > +aarch64_load_tuning_params_from_json (const char *data_filename,
> > + struct tune_params *tune);
> > +
> > +#endif
> > \ No newline at end of file
> > diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
> > index 198820b5e5f..a22369cfcb8 100644
> > --- a/gcc/config/aarch64/aarch64.cc
> > +++ b/gcc/config/aarch64/aarch64.cc
> > @@ -99,6 +99,7 @@
> > #include "ipa-fnsummary.h"
> > #include "hash-map.h"
> > #include "aarch64-json-tunings-printer.h"
> > +#include "aarch64-json-tunings-parser.h"
> >
> > /* This file should be included last. */
> > #include "target-def.h"
> > @@ -19006,6 +19007,21 @@ aarch64_override_options_internal (struct
> > gcc_options *opts)
> > aarch64_parse_override_string (opts->x_aarch64_override_tune_string,
> > &aarch64_tune_params);
> >
> > + /* We need to parse the JSON file only once per program execution. */
> > + if (opts->x_muser_provided_CPU)
> > + {
> > + static bool json_parsed = false;
> > + static struct tune_params aarch64_json_params;
> > + if (!json_parsed)
> > + {
> > + aarch64_json_params = *(tune->tune);
> > + aarch64_load_tuning_params_from_json (opts-
> > >x_muser_provided_CPU,
> > + &aarch64_json_params);
> > + json_parsed = true;
> > + }
> > + aarch64_tune_params = aarch64_json_params;
> > + }
> > +
I think we can avoid these singletons if we declare a variable
struct tune_params aarch64_json_tune_params = {};
in aarch64.cc (near the generic one, on line 591)
and then declare the hook TARGET_OPTION_INIT_STRUCT
and do the parsing there just once.
But I'm curious about
> > + aarch64_json_params = *(tune->tune);
Which seems to indicate that you seed the parsing with
an existing tuning.
I'd had expected
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index 8b2b1780475..3676c764054 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -19059,10 +19059,10 @@ aarch64_override_options_internal (struct gcc_options
*opts)
if (opts->x_muser_provided_CPU)
{
static bool json_parsed = false;
- static struct tune_params aarch64_json_params;
+ static struct tune_params aarch64_json_params = {};
if (!json_parsed)
{
- aarch64_json_params = *(tune->tune);
+ //aarch64_json_params = *(tune->tune);
aarch64_load_tuning_params_from_json (opts->x_muser_provided_CPU,
&aarch64_json_params);
json_parsed = true;
to work, but this ICEs further down because it seems some values are not
deserialized.
Am I missing something or does the serialization not sound tripped?
Thanks,
Tamar
> > if (opts->x_aarch64_ldp_policy_param)
> > aarch64_tune_params.ldp_policy_model = opts-
> > >x_aarch64_ldp_policy_param;
> >
> > diff --git a/gcc/config/aarch64/aarch64.opt
> > b/gcc/config/aarch64/aarch64.opt
> > index fd9864cc670..1735d2debe5 100644
> > --- a/gcc/config/aarch64/aarch64.opt
> > +++ b/gcc/config/aarch64/aarch64.opt
> > @@ -192,6 +192,10 @@ fdump-tuning-model=
> > Target Undocumented RejectNegative Negative(fdump-tuning-model=)
> > Joined Var(fdump_tuning_model)
> > -fdump-tuning-model=<filename> Dump current tuning model to a JSON
> file.
> >
> > +muser-provided-CPU=
> > +Target Undocumented RejectNegative Negative(muser-provided-CPU=)
> > Joined Var(muser_provided_CPU)
> > +-muser-provided-CPU=<json-tuning-file> User specific CPU tunings.
> > +
> > moverride=
> > Target RejectNegative ToLower Joined Var(aarch64_override_tune_string)
> > Save
> > -moverride=<string> Power users only! Override CPU optimization
> > parameters.
> > diff --git a/gcc/config/aarch64/t-aarch64 b/gcc/config/aarch64/t-aarch64
> > index fe95eaee116..ce1e54aba2f 100644
> > --- a/gcc/config/aarch64/t-aarch64
> > +++ b/gcc/config/aarch64/t-aarch64
> > @@ -211,6 +211,16 @@ aarch64-json-tunings-printer.o:
> > $(srcdir)/config/aarch64/aarch64-json-tunings-pr
> > $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS)
> > $(INCLUDES) \
> > $(srcdir)/config/aarch64/aarch64-json-tunings-printer.cc
> >
> > +aarch64-json-tunings-parser.o: $(srcdir)/config/aarch64/aarch64-json-
> > tunings-parser.cc \
> > + $(CONFIG_H) $(SYSTEM_H) $(CORETYPES_H) $(TM_H)
> > $(DIAGNOSTIC_CORE_H) \
> > + json-parsing.h \
> > + $(srcdir)/config/aarch64/aarch64-json-schema.h \
> > + $(srcdir)/config/aarch64/aarch64-json-tunings-parser.h \
> > + $(srcdir)/config/aarch64/aarch64-protos.h \
> > + $(srcdir)/config/arm/aarch-common-protos.h
> > + $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS)
> > $(INCLUDES) \
> > + $(srcdir)/config/aarch64/aarch64-json-tunings-parser.cc
> > +
> > comma=,
> > MULTILIB_OPTIONS = $(subst $(comma),/, $(patsubst %, mabi=%,
> $(subst
> > $(comma),$(comma)mabi=,$(TM_MULTILIB_CONFIG))))
> > MULTILIB_DIRNAMES = $(subst $(comma), ,$(TM_MULTILIB_CONFIG))
> > diff --git a/gcc/selftest-run-tests.cc b/gcc/selftest-run-tests.cc
> > index c68d3e79a31..e060f260a46 100644
> > --- a/gcc/selftest-run-tests.cc
> > +++ b/gcc/selftest-run-tests.cc
> > @@ -77,6 +77,7 @@ selftest::run_tests ()
> > opts_cc_tests ();
> > json_cc_tests ();
> > json_parser_cc_tests ();
> > + json_tunings_tests ();
> > cgraph_cc_tests ();
> > optinfo_emit_json_cc_tests ();
> > ordered_hash_map_tests_cc_tests ();
> > diff --git a/gcc/selftest.h b/gcc/selftest.h
> > index 4501d34181c..7a2fb3d14f1 100644
> > --- a/gcc/selftest.h
> > +++ b/gcc/selftest.h
> > @@ -239,6 +239,7 @@ extern void input_cc_tests ();
> > extern void ipa_modref_tree_cc_tests ();
> > extern void json_cc_tests ();
> > extern void json_parser_cc_tests ();
> > +extern void json_tunings_tests ();
> > extern void opt_suggestions_cc_tests ();
> > extern void optinfo_emit_json_cc_tests ();
> > extern void opts_cc_tests ();
> > --
> > 2.44.0