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 binary search of the `sysreg_names' structure populated from the `aarch64_system_regs.def' file via `match_reg'. 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. NOTE: For registers specified via their encoding (e.g. `S3_0_C4_C0_0'), once the encoding value is deemed valid (as per step 1) no further checks such as read/write support or architectural feature requirements are done and this second step is skipped, as is done in gas. gcc/ChangeLog: * gcc/config/aarch64/aarch64-protos.h (aarch64_valid_sysreg_name_p): New. (aarch64_retrieve_sysreg): Likewise. * gcc/config/aarch64/aarch64.cc (match_reg): Likewise. (is_implem_def_reg): Likewise. (aarch64_valid_sysreg_name_p): Likewise. (aarch64_retrieve_sysreg): Likewise. (aarch64_sysreg_valid_for_rw_p): Likewise. * gcc/config/aarch64/predicates.md (aarch64_sysreg_string): New. --- gcc/config/aarch64/aarch64-protos.h | 2 + gcc/config/aarch64/aarch64.cc | 121 ++++++++++++++++++++++++++++ gcc/config/aarch64/predicates.md | 4 + 3 files changed, 127 insertions(+) diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h index 60a55f4bc19..a134e2fcf8e 100644 --- a/gcc/config/aarch64/aarch64-protos.h +++ b/gcc/config/aarch64/aarch64-protos.h @@ -830,6 +830,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 (char *, bool); rtx aarch64_check_zero_based_sve_index_immediate (rtx); bool aarch64_sve_index_immediate_p (rtx); bool aarch64_sve_arith_immediate_p (machine_mode, rtx, bool); diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc index 030b39ded1a..dd5ac1cbc8d 100644 --- a/gcc/config/aarch64/aarch64.cc +++ b/gcc/config/aarch64/aarch64.cc @@ -28070,6 +28070,127 @@ aarch64_pars_overlap_p (rtx par1, rtx par2) return false; } +/* Binary search of a user-supplied system register name against + a database of known register names. Upon match the index of + hit in database is returned, else return -1. */ +int +match_reg (const char *ref, const char *database[], int db_len) +{ + /* Check for named system registers. */ + int imin = 0, imax = db_len - 1, mid, cmp_res; + while (imin <= imax) + { + mid = (imin + imax) / 2; + + cmp_res = strcmp (ref, database[mid]); + if (cmp_res == 0) + return mid; + else if (cmp_res > 0) + imin = mid+1; + else + imax = mid-1; + } + return -1; +} + +/* Parse an implementation-defined system register name of + the form S[0-3]_[0-7]_C[0-15]_C[0-15]_[1-7]. + Return true if name matched against above pattern, false + otherwise. */ +bool +is_implem_def_reg (const char *regname) +{ + /* Check for implementation-defined system registers. */ + int name_len = strlen (regname); + if (name_len < 12 || name_len > 14) + return false; + + int pos = 0, i = 0, j = 0; + char n[3] = {0}, m[3] = {0}; + if (regname[pos] != 's' && 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 (regname[pos] != 'c' && regname[pos] != 'C') + return false; + pos++; + while (regname[pos] != '_') + { + if (i > 2) + return false; + if (!ISDIGIT (regname[pos])) + return false; + n[i++] = regname[pos++]; + } + if (atoi (n) > 15) + return false; + if (regname[pos++] != '_') + return false; + if (regname[pos] != 'c' && regname[pos] != 'C') + return false; + pos++; + while (regname[pos] != '_') + { + if (j > 2) + return false; + if (!ISDIGIT (regname[pos])) + return false; + m[j++] = regname[pos++]; + } + if (atoi (m) > 15) + return false; + if (regname[pos++] != '_') + return false; + if (regname[pos] < '0' || regname[pos] > '7') + return false; + return true; +} + +/* Ensure a supplied system register name is implemented in the target + architecture. For use in back-end predicate `aarch64_sysreg_string'. */ +bool +aarch64_valid_sysreg_name_p (const char *regname) +{ + int reg_id = match_reg (regname, sysreg_names, nsysreg); + if (reg_id == -1) + return is_implem_def_reg (regname); + return (aarch64_isa_flags & sysreg_reqs[reg_id]); +} + +/* Ensure a supplied system register name is suitable for the desired use. + This involves checking its suitability for the requested read/write + operation and that it is implemented in the target architecture. + + NOTE: The read/write flags refer to whether a given register is + read/write-only. */ +const char * +aarch64_retrieve_sysreg (char *regname, bool write_p) +{ + int reg_id = match_reg (regname, sysreg_names, nsysreg); + if (reg_id == -1) + { + if (is_implem_def_reg (regname)) + return (const char *) regname; + else + return NULL; + } + if ((write_p && (sysreg_properties[reg_id] & F_REG_READ)) + || (!write_p && (sysreg_properties[reg_id] & F_REG_WRITE))) + return NULL; + if (aarch64_isa_flags & sysreg_reqs[reg_id]) + return sysreg_names_generic[reg_id]; + return NULL; +} + /* Target-specific selftests. */ #if CHECKING_P diff --git a/gcc/config/aarch64/predicates.md b/gcc/config/aarch64/predicates.md index 01de4743974..5f0d242e4a8 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") -- 2.41.0