[AArch64] Check 128-bit Sysreg Builtins
authorArchibald Elliott <archibald.elliott@arm.com>
Tue, 20 Dec 2022 16:08:06 +0000 (16:08 +0000)
committerArchibald Elliott <archibald.elliott@arm.com>
Mon, 23 Jan 2023 15:24:30 +0000 (15:24 +0000)
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

clang/include/clang/Basic/BuiltinsAArch64.def
clang/lib/Headers/arm_acle.h
clang/lib/Sema/SemaChecking.cpp
clang/test/CodeGen/aarch64-sysregs-target.c [new file with mode: 0644]
clang/test/Sema/aarch64-special-register.c

index 61e6a51..e6672a1 100644 (file)
@@ -82,11 +82,11 @@ BUILTIN(__builtin_arm_prefetch, "vvC*UiUiUiUi", "nc")
 // System Registers
 BUILTIN(__builtin_arm_rsr, "UicC*", "nc")
 BUILTIN(__builtin_arm_rsr64, "WUicC*", "nc")
-BUILTIN(__builtin_arm_rsr128, "LLLUicC*", "nc")
+TARGET_BUILTIN(__builtin_arm_rsr128, "LLLUicC*", "nc", "d128")
 BUILTIN(__builtin_arm_rsrp, "v*cC*", "nc")
 BUILTIN(__builtin_arm_wsr, "vcC*Ui", "nc")
 BUILTIN(__builtin_arm_wsr64, "vcC*WUi", "nc")
-BUILTIN(__builtin_arm_wsr128, "vcC*LLLUi", "nc")
+TARGET_BUILTIN(__builtin_arm_wsr128, "vcC*LLLUi", "nc", "d128")
 BUILTIN(__builtin_arm_wsrp, "vcC*vC*", "nc")
 
 // MSVC
index 469f24e..e086f1f 100644 (file)
@@ -712,17 +712,13 @@ __arm_st64bv0(void *__addr, data512_t __value) {
 /* 10.1 Special register intrinsics */
 #define __arm_rsr(sysreg) __builtin_arm_rsr(sysreg)
 #define __arm_rsr64(sysreg) __builtin_arm_rsr64(sysreg)
-#if __ARM_FEATURE_SYSREG128
 #define __arm_rsr128(sysreg) __builtin_arm_rsr128(sysreg)
-#endif
 #define __arm_rsrp(sysreg) __builtin_arm_rsrp(sysreg)
 #define __arm_rsrf(sysreg) __builtin_bit_cast(float, __arm_rsr(sysreg))
 #define __arm_rsrf64(sysreg) __builtin_bit_cast(double, __arm_rsr64(sysreg))
 #define __arm_wsr(sysreg, v) __builtin_arm_wsr(sysreg, v)
 #define __arm_wsr64(sysreg, v) __builtin_arm_wsr64(sysreg, v)
-#if __ARM_FEATURE_SYSREG128
 #define __arm_wsr128(sysreg, v) __builtin_arm_wsr128(sysreg, v)
-#endif
 #define __arm_wsrp(sysreg, v) __builtin_arm_wsrp(sysreg, v)
 #define __arm_wsrf(sysreg, v) __arm_wsr(sysreg, __builtin_bit_cast(uint32_t, v))
 #define __arm_wsrf64(sysreg, v) __arm_wsr64(sysreg, __builtin_bit_cast(uint64_t, v))
index 07325d5..ea21171 100644 (file)
@@ -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;
diff --git a/clang/test/CodeGen/aarch64-sysregs-target.c b/clang/test/CodeGen/aarch64-sysregs-target.c
new file mode 100644 (file)
index 0000000..ec69a67
--- /dev/null
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +v8a -fsyntax-only -verify -emit-llvm -o - %s
+// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +d128 -fsyntax-only -verify=d128 -emit-llvm -o - %s
+
+// REQUIRES: aarch64-registered-target
+
+// Test that functions with the correct target attributes can use the correct
+// system-register intriniscs.
+
+// All the calls below are valid if you have -target-feature +d128
+// d128-no-diagnostics
+
+#include <arm_acle.h>
+
+void anytarget(void) {
+  unsigned x = __arm_rsr("1:2:3:4:5");
+  __arm_wsr("1:2:3:4:5", x);
+  unsigned long y = __arm_rsr64("1:2:3:4:5");
+  __arm_wsr64("1:2:3:4:5", y);
+  void *p = __arm_rsrp("1:2:3:4:5");
+  __arm_wsrp("1:2:3:4:5", p);
+}
+
+__attribute__((target("d128")))
+void d128target(void) {
+  __uint128_t x = __arm_rsr128("1:2:3:4:5");
+  __arm_wsr128("1:2:3:4:5", x);
+}
+
+void notd128target(void) {
+  __uint128_t x = __arm_rsr128("1:2:3:4:5"); // expected-error {{needs target feature d128}}
+  __arm_wsr128("1:2:3:4:5", x); // expected-error {{needs target feature d128}}
+}
index e251236..4d2cfd8 100644 (file)
@@ -16,6 +16,10 @@ void wsr64_1(unsigned long v) {
   __builtin_arm_wsr64("sysreg", v);
 }
 
+void wsr128_1(__uint128_t v) {
+  __builtin_arm_wsr128("sysreg", v);
+}
+
 unsigned rsr_1(void) {
   return __builtin_arm_rsr("sysreg");
 }
@@ -28,6 +32,10 @@ unsigned long rsr64_1(void) {
   return __builtin_arm_rsr64("sysreg");
 }
 
+__uint128_t rsr128_1(void) {
+  return __builtin_arm_rsr128("sysreg");
+}
+
 void wsr_2(unsigned v) {
   __builtin_arm_wsr("0:1:2:3:4", v);
 }
@@ -52,6 +60,10 @@ unsigned long rsr64_2(void) {
   return __builtin_arm_rsr64("0:1:15:15:4");
 }
 
+__uint128_t rsr128_2(void) {
+  return __builtin_arm_rsr128("0:1:15:15:4");
+}
+
 void wsr_3(unsigned v) {
   __builtin_arm_wsr("0:1:2", v); //expected-error {{invalid special register for builtin}}
 }
@@ -64,6 +76,10 @@ void wsr64_3(unsigned long v) {
   __builtin_arm_wsr64("0:1:2", v); //expected-error {{invalid special register for builtin}}
 }
 
+void wsr128_3(__uint128_t v) {
+  __builtin_arm_wsr128("0:1:2", v); //expected-error {{invalid special register for builtin}}
+}
+
 unsigned rsr_3(void) {
   return __builtin_arm_rsr("0:1:2"); //expected-error {{invalid special register for builtin}}
 }
@@ -99,3 +115,101 @@ unsigned long rsr64_5(void) {
 unsigned long rsr64_6(void) {
   return __builtin_arm_rsr64("0:1:16:16:2"); //expected-error {{invalid special register for builtin}}
 }
+
+__uint128_t rsr128_3(void) {
+  return __builtin_arm_rsr128("0:1:2"); //expected-error {{invalid special register for builtin}}
+}
+
+__uint128_t rsr128_4(void) {
+  return __builtin_arm_rsr128("0:1:2:3:8"); //expected-error {{invalid special register for builtin}}
+}
+
+__uint128_t rsr128_5(void) {
+  return __builtin_arm_rsr128("0:8:2:3:4"); //expected-error {{invalid special register for builtin}}
+}
+
+__uint128_t rsr128_6(void) {
+  return __builtin_arm_rsr128("0:1:16:16:2"); //expected-error {{invalid special register for builtin}}
+}
+
+void wsr_4(void) {
+  __builtin_arm_wsr("spsel", 15);
+  __builtin_arm_wsr("daifclr", 15);
+  __builtin_arm_wsr("daifset", 15);
+  __builtin_arm_wsr("pan", 15);
+  __builtin_arm_wsr("uao", 15);
+  __builtin_arm_wsr("dit", 15);
+  __builtin_arm_wsr("ssbs", 15);
+  __builtin_arm_wsr("tco", 15);
+
+  __builtin_arm_wsr("allint", 1);
+  __builtin_arm_wsr("pm", 1);
+}
+
+void wsr64_4(void) {
+  __builtin_arm_wsr("spsel", 15);
+  __builtin_arm_wsr("daifclr", 15);
+  __builtin_arm_wsr("daifset", 15);
+  __builtin_arm_wsr("pan", 15);
+  __builtin_arm_wsr("uao", 15);
+  __builtin_arm_wsr("dit", 15);
+  __builtin_arm_wsr("ssbs", 15);
+  __builtin_arm_wsr("tco", 15);
+
+  __builtin_arm_wsr("allint", 1);
+  __builtin_arm_wsr("pm", 1);
+}
+
+void wsr_5(unsigned v) {
+  __builtin_arm_wsr("spsel", v); // expected-error {{must be a constant integer}}
+  __builtin_arm_wsr("daifclr", v); // expected-error {{must be a constant integer}}
+  __builtin_arm_wsr("daifset", v); // expected-error {{must be a constant integer}}
+  __builtin_arm_wsr("pan", v); // expected-error {{must be a constant integer}}
+  __builtin_arm_wsr("uao", v); // expected-error {{must be a constant integer}}
+  __builtin_arm_wsr("dit", v); // expected-error {{must be a constant integer}}
+  __builtin_arm_wsr("ssbs", v); // expected-error {{must be a constant integer}}
+  __builtin_arm_wsr("tco", v); // expected-error {{must be a constant integer}}
+  __builtin_arm_wsr("allint", v); // expected-error {{must be a constant integer}}
+  __builtin_arm_wsr("pm", v); // expected-error {{must be a constant integer}}
+}
+
+void wsr64_5(unsigned long v) {
+  __builtin_arm_wsr64("spsel", v); // expected-error {{must be a constant integer}}
+  __builtin_arm_wsr64("daifclr", v); // expected-error {{must be a constant integer}}
+  __builtin_arm_wsr64("daifset", v); // expected-error {{must be a constant integer}}
+  __builtin_arm_wsr64("pan", v); // expected-error {{must be a constant integer}}
+  __builtin_arm_wsr64("uao", v); // expected-error {{must be a constant integer}}
+  __builtin_arm_wsr64("dit", v); // expected-error {{must be a constant integer}}
+  __builtin_arm_wsr64("ssbs", v); // expected-error {{must be a constant integer}}
+  __builtin_arm_wsr64("tco", v); // expected-error {{must be a constant integer}}
+  __builtin_arm_wsr64("allint", v); // expected-error {{must be a constant integer}}
+  __builtin_arm_wsr64("pm", v); // expected-error {{must be a constant integer}}
+}
+
+void wsr_6(void) {
+  __builtin_arm_wsr("spsel", 16); // expected-error {{outside the valid range}}
+  __builtin_arm_wsr("daifclr", 16); // expected-error {{outside the valid range}}
+  __builtin_arm_wsr("daifset", 16); // expected-error {{outside the valid range}}
+  __builtin_arm_wsr("pan", 16); // expected-error {{outside the valid range}}
+  __builtin_arm_wsr("uao", 16); // expected-error {{outside the valid range}}
+  __builtin_arm_wsr("dit", 16); // expected-error {{outside the valid range}}
+  __builtin_arm_wsr("ssbs", 16); // expected-error {{outside the valid range}}
+  __builtin_arm_wsr("tco", 16); // expected-error {{outside the valid range}}
+
+  __builtin_arm_wsr("allint", 2); // expected-error {{outside the valid range}}
+  __builtin_arm_wsr("pm", 2); // expected-error {{outside the valid range}}
+}
+
+void wsr64_6(void) {
+  __builtin_arm_wsr64("spsel", 16); // expected-error {{outside the valid range}}
+  __builtin_arm_wsr64("daifclr", 16); // expected-error {{outside the valid range}}
+  __builtin_arm_wsr64("daifset", 16); // expected-error {{outside the valid range}}
+  __builtin_arm_wsr64("pan", 16); // expected-error {{outside the valid range}}
+  __builtin_arm_wsr64("uao", 16); // expected-error {{outside the valid range}}
+  __builtin_arm_wsr64("dit", 16); // expected-error {{outside the valid range}}
+  __builtin_arm_wsr64("ssbs", 16); // expected-error {{outside the valid range}}
+  __builtin_arm_wsr64("tco", 16); // expected-error {{outside the valid range}}
+
+  __builtin_arm_wsr64("allint", 2); // expected-error {{outside the valid range}}
+  __builtin_arm_wsr64("pm", 2); // expected-error {{outside the valid range}}
+}