diff options
author | Archibald Elliott <archibald.elliott@arm.com> | 2022-12-20 16:08:06 +0000 |
---|---|---|
committer | Archibald Elliott <archibald.elliott@arm.com> | 2023-01-23 15:24:30 +0000 |
commit | 03ef89f8d9094bf70416e19d0e909909b100a4a0 (patch) | |
tree | f4d665664968067483587a2acf7232529e233eef /clang/lib/Sema/SemaChecking.cpp | |
parent | 91b9c0cd6b9f9bae0753bf7e41188a55a5ee1dfb (diff) | |
download | llvm-03ef89f8d9094bf70416e19d0e909909b100a4a0.zip llvm-03ef89f8d9094bf70416e19d0e909909b100a4a0.tar.gz llvm-03ef89f8d9094bf70416e19d0e909909b100a4a0.tar.bz2 |
[AArch64] Check 128-bit Sysreg Builtins
This patch contains several related changes:
1. We move to using TARGET_BUILTIN for the 128-bit system register
builtins to give better error messages when d128 has not been
enabled, or has been enabled in a per-function manner.
2. We now validate the inputs to the 128-bit system register builtins,
like we validate the other system register builtins.
3. We update the list of named PSTATE accessors for MSR (immediate), and
now correctly enforce the expected ranges of the immediates. There is
a long comment about how we chose to do this to comply with the ACLE
when most of the PSTATE accessors for MSR (immediate) have aliased
system registers for MRS/MSR which expect different values. In short,
the MSR (immediate) names are prioritised, rather than falling-back
to the register form when the value is out of range.
Differential Revision: https://reviews.llvm.org/D140222
Diffstat (limited to 'clang/lib/Sema/SemaChecking.cpp')
-rw-r--r-- | clang/lib/Sema/SemaChecking.cpp | 56 |
1 files changed, 45 insertions, 11 deletions
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 07325d5..ea21171 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -3292,7 +3292,9 @@ bool Sema::CheckAArch64BuiltinFunctionCall(const TargetInfo &TI, } if (BuiltinID == AArch64::BI__builtin_arm_rsr64 || - BuiltinID == AArch64::BI__builtin_arm_wsr64) + BuiltinID == AArch64::BI__builtin_arm_wsr64 || + BuiltinID == AArch64::BI__builtin_arm_rsr128 || + BuiltinID == AArch64::BI__builtin_arm_wsr128) return SemaBuiltinARMSpecialReg(BuiltinID, TheCall, 0, 5, true); // Memory Tagging Extensions (MTE) Intrinsics @@ -8314,6 +8316,8 @@ bool Sema::SemaBuiltinARMSpecialReg(unsigned BuiltinID, CallExpr *TheCall, BuiltinID == ARM::BI__builtin_arm_wsrp; bool IsAArch64Builtin = BuiltinID == AArch64::BI__builtin_arm_rsr64 || BuiltinID == AArch64::BI__builtin_arm_wsr64 || + BuiltinID == AArch64::BI__builtin_arm_rsr128 || + BuiltinID == AArch64::BI__builtin_arm_wsr128 || BuiltinID == AArch64::BI__builtin_arm_rsr || BuiltinID == AArch64::BI__builtin_arm_rsrp || BuiltinID == AArch64::BI__builtin_arm_wsr || @@ -8381,21 +8385,51 @@ bool Sema::SemaBuiltinARMSpecialReg(unsigned BuiltinID, CallExpr *TheCall, return Diag(TheCall->getBeginLoc(), diag::err_arm_invalid_specialreg) << Arg->getSourceRange(); } else if (IsAArch64Builtin && Fields.size() == 1) { - // If the register name is one of those that appear in the condition below - // and the special register builtin being used is one of the write builtins, - // then we require that the argument provided for writing to the register - // is an integer constant expression. This is because it will be lowered to - // an MSR (immediate) instruction, so we need to know the immediate at - // compile time. + // This code validates writes to PSTATE registers. + + // Not a write. if (TheCall->getNumArgs() != 2) return false; - std::string RegLower = Reg.lower(); - if (RegLower != "spsel" && RegLower != "daifset" && RegLower != "daifclr" && - RegLower != "pan" && RegLower != "uao") + // The 128-bit system register accesses do not touch PSTATE. + if (BuiltinID == AArch64::BI__builtin_arm_rsr128 || + BuiltinID == AArch64::BI__builtin_arm_wsr128) return false; - return SemaBuiltinConstantArgRange(TheCall, 1, 0, 15); + // These are the named PSTATE accesses using "MSR (immediate)" instructions, + // along with the upper limit on the immediates allowed. + auto MaxLimit = llvm::StringSwitch<std::optional<unsigned>>(Reg) + .CaseLower("spsel", 15) + .CaseLower("daifclr", 15) + .CaseLower("daifset", 15) + .CaseLower("pan", 15) + .CaseLower("uao", 15) + .CaseLower("dit", 15) + .CaseLower("ssbs", 15) + .CaseLower("tco", 15) + .CaseLower("allint", 1) + .CaseLower("pm", 1) + .Default(std::nullopt); + + // If this is not a named PSTATE, just continue without validating, as this + // will be lowered to an "MSR (register)" instruction directly + if (!MaxLimit) + return false; + + // Here we only allow constants in the range for that pstate, as required by + // the ACLE. + // + // While clang also accepts the names of system registers in its ACLE + // intrinsics, we prevent this with the PSTATE names used in MSR (immediate) + // as the value written via a register is different to the value used as an + // immediate to have the same effect. e.g., for the instruction `msr tco, + // x0`, it is bit 25 of register x0 that is written into PSTATE.TCO, but + // with `msr tco, #imm`, it is bit 0 of xN that is written into PSTATE.TCO. + // + // If a programmer wants to codegen the MSR (register) form of `msr tco, + // xN`, they can still do so by specifying the register using five + // colon-separated numbers in a string. + return SemaBuiltinConstantArgRange(TheCall, 1, 0, *MaxLimit); } return false; |