From: Victor Do Nascimento Date: Mon, 26 Jun 2023 12:10:09 +0000 (+0100) Subject: aarch64: Implement system register validation tools X-Git-Tag: basepoints/gcc-15~3925 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7d36ea705712539d2c2cda668feb2e675a74958e;p=thirdparty%2Fgcc.git aarch64: Implement system register validation tools Given the implementation of a mechanism of encoding system registers into GCC, this patch provides the mechanism of validating their use by the compiler. In particular, this involves: 1. Ensuring a supplied string corresponds to a known system register name. System registers can be accessed either via their name (e.g. `SPSR_EL1') or their encoding (e.g. `S3_0_C4_C0_0'). Register names are validated using a hash map, mapping known system register names to its corresponding `sysreg_t' struct, which is populated from the `aarch64_system_regs.def' file. Register name validation is done via `lookup_sysreg_map', while the encoding naming convention is validated via a parser implemented in this patch - `is_implem_def_reg'. 2. Once a given register name is deemed to be valid, it is checked against a further 2 criteria: a. Is the referenced register implemented in the target architecture? This is achieved by comparing the ARCH field in the relevant SYSREG entry from `aarch64_system_regs.def' against `aarch64_feature_flags' flags set at compile-time. b. Is the register being used correctly? Check the requested operation against the FLAGS specified in SYSREG. This prevents operations like writing to a read-only system register. gcc/ChangeLog: * config/aarch64/aarch64-protos.h (aarch64_valid_sysreg_name_p): New. (aarch64_retrieve_sysreg): Likewise. * config/aarch64/aarch64.cc (is_implem_def_reg): Likewise. (aarch64_valid_sysreg_name_p): Likewise. (aarch64_retrieve_sysreg): Likewise. (aarch64_register_sysreg): Likewise. (aarch64_init_sysregs): Likewise. (aarch64_lookup_sysreg_map): Likewise. * config/aarch64/predicates.md (aarch64_sysreg_string): New. --- diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h index 25a9103f0e7d..9e09cf7ee58f 100644 --- a/gcc/config/aarch64/aarch64-protos.h +++ b/gcc/config/aarch64/aarch64-protos.h @@ -839,6 +839,8 @@ bool aarch64_simd_shift_imm_p (rtx, machine_mode, bool); bool aarch64_sve_ptrue_svpattern_p (rtx, struct simd_immediate_info *); bool aarch64_simd_valid_immediate (rtx, struct simd_immediate_info *, enum simd_immediate_check w = AARCH64_CHECK_MOV); +bool aarch64_valid_sysreg_name_p (const char *); +const char *aarch64_retrieve_sysreg (const char *, bool); rtx aarch64_check_zero_based_sve_index_immediate (rtx); bool aarch64_maybe_generate_simd_constant (rtx, rtx, machine_mode); bool aarch64_simd_special_constant_p (rtx, machine_mode); diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc index 91ec1d275587..737fc696deff 100644 --- a/gcc/config/aarch64/aarch64.cc +++ b/gcc/config/aarch64/aarch64.cc @@ -91,6 +91,7 @@ #include "symbol-summary.h" #include "ipa-prop.h" #include "ipa-fnsummary.h" +#include "hash-map.h" /* This file should be included last. */ #include "target-def.h" @@ -510,6 +511,52 @@ const sysreg_t aarch64_sysregs[] = #undef AARCH64_NO_FEATURES +using sysreg_map_t = hash_map; +static sysreg_map_t *sysreg_map = nullptr; + +/* Map system register names to their hardware metadata: encoding, + feature flags and architectural feature requirements, all of which + are encoded in a sysreg_t struct. */ +void +aarch64_register_sysreg (const char *name, const sysreg_t *metadata) +{ + bool dup = sysreg_map->put (name, metadata); + gcc_checking_assert (!dup); +} + +/* Lazily initialize hash table for system register validation, + checking the validity of supplied register name and returning + register's associated metadata. */ +static void +aarch64_init_sysregs (void) +{ + gcc_assert (!sysreg_map); + sysreg_map = new sysreg_map_t; + + + for (unsigned i = 0; i < ARRAY_SIZE (aarch64_sysregs); i++) + { + const sysreg_t *reg = aarch64_sysregs + i; + aarch64_register_sysreg (reg->name, reg); + } +} + +/* No direct access to the sysreg hash-map should be made. Doing so + risks trying to acess an unitialized hash-map and dereferencing the + returned double pointer without due care risks dereferencing a + null-pointer. */ +const sysreg_t * +aarch64_lookup_sysreg_map (const char *regname) +{ + if (!sysreg_map) + aarch64_init_sysregs (); + + const sysreg_t **sysreg_entry = sysreg_map->get (regname); + if (sysreg_entry != NULL) + return *sysreg_entry; + return NULL; +} + /* The current tuning set. */ struct tune_params aarch64_tune_params = generic_tunings; @@ -28955,6 +29002,106 @@ make_pass_switch_pstate_sm (gcc::context *ctxt) return new pass_switch_pstate_sm (ctxt); } +/* Parse an implementation-defined system register name of + the form S[0-3]_[0-7]_C[0-15]_C[0-15]_[0-7]. + Return true if name matched against above pattern, false + otherwise. */ +bool +aarch64_is_implem_def_reg (const char *regname) +{ + unsigned pos = 0; + unsigned name_len = strlen (regname); + if (name_len < 12 || name_len > 14) + return false; + + auto cterm_valid_p = [&]() + { + bool leading_zero_p = false; + unsigned i = 0; + char n[3] = {0}; + + if (regname[pos] != 'c') + return false; + pos++; + while (regname[pos] != '_') + { + if (leading_zero_p) + return false; + if (i == 0 && regname[pos] == '0') + leading_zero_p = true; + if (i > 2) + return false; + if (!ISDIGIT (regname[pos])) + return false; + n[i++] = regname[pos++]; + } + if (atoi (n) > 15) + return false; + return true; + }; + + if (regname[pos] != 's') + return false; + pos++; + if (regname[pos] < '0' || regname[pos] > '3') + return false; + pos++; + if (regname[pos++] != '_') + return false; + if (regname[pos] < '0' || regname[pos] > '7') + return false; + pos++; + if (regname[pos++] != '_') + return false; + if (!cterm_valid_p ()) + return false; + if (regname[pos++] != '_') + return false; + if (!cterm_valid_p ()) + return false; + if (regname[pos++] != '_') + return false; + if (regname[pos] < '0' || regname[pos] > '7') + return false; + return true; +} + +/* Return true if REGNAME matches either a known permitted system + register name, or a generic sysreg specification. For use in + back-end predicate `aarch64_sysreg_string'. */ +bool +aarch64_valid_sysreg_name_p (const char *regname) +{ + const sysreg_t *sysreg = aarch64_lookup_sysreg_map (regname); + if (sysreg == NULL) + return aarch64_is_implem_def_reg (regname); + if (sysreg->arch_reqs) + return (aarch64_isa_flags & sysreg->arch_reqs); + return true; +} + +/* Return the generic sysreg specification for a valid system register + name, otherwise NULL. WRITE_P is true iff the register is being + written to. */ +const char * +aarch64_retrieve_sysreg (const char *regname, bool write_p) +{ + const sysreg_t *sysreg = aarch64_lookup_sysreg_map (regname); + if (sysreg == NULL) + { + if (aarch64_is_implem_def_reg (regname)) + return regname; + else + return NULL; + } + if ((write_p && (sysreg->properties & F_REG_READ)) + || (!write_p && (sysreg->properties & F_REG_WRITE))) + return NULL; + if ((~aarch64_isa_flags & sysreg->arch_reqs) != 0) + return NULL; + return sysreg->encoding; +} + /* Target-specific selftests. */ #if CHECKING_P diff --git a/gcc/config/aarch64/predicates.md b/gcc/config/aarch64/predicates.md index c60a9e19c701..9af28103a744 100644 --- a/gcc/config/aarch64/predicates.md +++ b/gcc/config/aarch64/predicates.md @@ -20,6 +20,10 @@ (include "../arm/common.md") +(define_predicate "aarch64_sysreg_string" + (and (match_code "const_string") + (match_test "aarch64_valid_sysreg_name_p (XSTR (op, 0))"))) + (define_special_predicate "cc_register" (and (match_code "reg") (and (match_test "REGNO (op) == CC_REGNUM")