[clang] 9426fc1 - [AArch64] Fix _sys implemantation and MRS/MSR Sema checks (#187290)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 23 03:12:46 PDT 2026
Author: Lukacma
Date: 2026-03-23T10:12:41Z
New Revision: 9426fc19afb106ff964a43ab7f3c7a0be940111c
URL: https://github.com/llvm/llvm-project/commit/9426fc19afb106ff964a43ab7f3c7a0be940111c
DIFF: https://github.com/llvm/llvm-project/commit/9426fc19afb106ff964a43ab7f3c7a0be940111c.diff
LOG: [AArch64] Fix _sys implemantation and MRS/MSR Sema checks (#187290)
This patch fixes lowering of _sys builtin, which used to lower into
invalid MSR S1... instruction. This was fixed by adding new sys llvm
intrinsic and proper lowering into sys instruction and its aliases.
I also fixed the sema check for _sys, _ReadStatusRegister and
_WriteStatusRegister builtins so they correctly capture invalid
usecases.
Added:
llvm/test/CodeGen/AArch64/aarch64-sys-intrinsic.ll
Modified:
clang/lib/CodeGen/TargetBuiltins/ARM.cpp
clang/lib/Sema/SemaARM.cpp
clang/test/CodeGen/arm64-microsoft-sys.c
clang/test/Sema/builtins-microsoft-arm64.c
llvm/include/llvm/IR/IntrinsicsAArch64.td
llvm/lib/Target/AArch64/AArch64InstrFormats.td
llvm/lib/Target/AArch64/AArch64InstrInfo.td
llvm/test/tools/llvm-mca/AArch64/Neoverse/V1-misc-instructions.s
Removed:
################################################################################
diff --git a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
index ed157c198c510..8ec2f5b83085c 100644
--- a/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/ARM.cpp
@@ -5022,20 +5022,15 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
}
if (BuiltinID == clang::AArch64::BI_ReadStatusReg ||
- BuiltinID == clang::AArch64::BI_WriteStatusReg ||
- BuiltinID == clang::AArch64::BI__sys) {
+ BuiltinID == clang::AArch64::BI_WriteStatusReg) {
LLVMContext &Context = CGM.getLLVMContext();
unsigned SysReg =
E->getArg(0)->EvaluateKnownConstInt(getContext()).getZExtValue();
std::string SysRegStr;
- unsigned SysRegOp0 = (BuiltinID == clang::AArch64::BI_ReadStatusReg ||
- BuiltinID == clang::AArch64::BI_WriteStatusReg)
- ? ((1 << 1) | ((SysReg >> 14) & 1))
- : 1;
llvm::raw_string_ostream(SysRegStr)
- << SysRegOp0 << ":" << ((SysReg >> 11) & 7) << ":"
+ << (0b10 | SysReg >> 14) << ":" << ((SysReg >> 11) & 7) << ":"
<< ((SysReg >> 7) & 15) << ":" << ((SysReg >> 3) & 15) << ":"
<< (SysReg & 7);
@@ -5055,14 +5050,28 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
llvm::Function *F = CGM.getIntrinsic(Intrinsic::write_register, Types);
llvm::Value *ArgValue = EmitScalarExpr(E->getArg(1));
llvm::Value *Result = Builder.CreateCall(F, {Metadata, ArgValue});
- if (BuiltinID == clang::AArch64::BI__sys) {
- // Return 0 for convenience, even though MSVC returns some other undefined
- // value.
- Result = ConstantInt::get(Builder.getInt32Ty(), 0);
- }
+
return Result;
}
+ if (BuiltinID == clang::AArch64::BI__sys) {
+ unsigned SysReg =
+ E->getArg(0)->EvaluateKnownConstInt(getContext()).getZExtValue();
+ const unsigned Op1 = SysReg >> 11;
+ const unsigned CRn = (SysReg >> 7) & 0xf;
+ const unsigned CRm = (SysReg >> 3) & 0xf;
+ const unsigned Op2 = SysReg & 0x7;
+
+ Builder.CreateCall(CGM.getIntrinsic(Intrinsic::aarch64_sys),
+ {Builder.getInt32(Op1), Builder.getInt32(CRn),
+ Builder.getInt32(CRm), Builder.getInt32(Op2),
+ EmitScalarExpr(E->getArg(1))});
+
+ // Return 0 for convenience, even though MSVC returns some other undefined
+ // value.
+ return ConstantInt::get(Builder.getInt32Ty(), 0);
+ }
+
if (BuiltinID == clang::AArch64::BI_AddressOfReturnAddress) {
llvm::Function *F =
CGM.getIntrinsic(Intrinsic::addressofreturnaddress, AllocaInt8PtrTy);
diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp
index f4b811af6a100..e54c8228e5ff8 100644
--- a/clang/lib/Sema/SemaARM.cpp
+++ b/clang/lib/Sema/SemaARM.cpp
@@ -1260,11 +1260,14 @@ bool SemaARM::CheckAArch64BuiltinFunctionCall(const TargetInfo &TI,
return BuiltinARMSpecialReg(BuiltinID, TheCall, 0, 5, true);
// Only check the valid encoding range. Any constant in this range would be
- // converted to a register of the form S1_2_C3_C4_5. Let the hardware throw
+ // converted to a register of the form S2_2_C3_C4_5. Let the hardware throw
// an exception for incorrect registers. This matches MSVC behavior.
if (BuiltinID == AArch64::BI_ReadStatusReg ||
- BuiltinID == AArch64::BI_WriteStatusReg || BuiltinID == AArch64::BI__sys)
- return SemaRef.BuiltinConstantArgRange(TheCall, 0, 0, 0x7fff);
+ BuiltinID == AArch64::BI_WriteStatusReg)
+ return SemaRef.BuiltinConstantArgRange(TheCall, 0, 0x4000, 0x7fff);
+
+ if (BuiltinID == AArch64::BI__sys)
+ return SemaRef.BuiltinConstantArgRange(TheCall, 0, 0, 0x3fff);
if (BuiltinID == AArch64::BI__getReg)
return SemaRef.BuiltinConstantArgRange(TheCall, 0, 0, 31);
diff --git a/clang/test/CodeGen/arm64-microsoft-sys.c b/clang/test/CodeGen/arm64-microsoft-sys.c
index 5913a02d933ae..0268584c5fd13 100644
--- a/clang/test/CodeGen/arm64-microsoft-sys.c
+++ b/clang/test/CodeGen/arm64-microsoft-sys.c
@@ -33,33 +33,27 @@ void check__sys(__int64 v) {
__int64 ret;
__sys(ARM64_DC_CGDSW_EL1, v);
-// CHECK-ASM: msr S1_0_C7_C10_6, x8
+// CHECK-ASM: sys #0, c7, c10, #6, x8
// CHECK-IR: %[[VAR:.*]] = load i64,
-// CHECK-IR-NEXT: call void @llvm.write_register.i64(metadata ![[MD2:.*]], i64 %[[VAR]])
+// CHECK-IR-NEXT: call void @llvm.aarch64.sys(i32 0, i32 7, i32 10, i32 6, i64 %[[VAR]])
__sys(ARM64_IC_IALLU_EL1, v);
-// CHECK-ASM: msr S1_0_C7_C5_0, x8
+// CHECK-ASM: sys #0, c7, c5, #0, x8
// CHECK-IR: %[[VAR:.*]] = load i64,
-// CHECK-IR-NEXT: call void @llvm.write_register.i64(metadata ![[MD3:.*]], i64 %[[VAR]])
+// CHECK-IR-NEXT: call void @llvm.aarch64.sys(i32 0, i32 7, i32 5, i32 0, i64 %[[VAR]])
__sys(ARM64_AT_S1E2W, v);
-// CHECK-ASM: msr S1_4_C7_C8_1, x8
+// CHECK-ASM: at s1e2w, x8
// CHECK-IR: %[[VAR:.*]] = load i64,
-// CHECK-IR-NEXT: call void @llvm.write_register.i64(metadata ![[MD4:.*]], i64 %[[VAR]])
+// CHECK-IR-NEXT: call void @llvm.aarch64.sys(i32 4, i32 7, i32 8, i32 1, i64 %[[VAR]])
__sys(ARM64_TLBI_VMALLE1, v);
-// CHECK-ASM: msr S1_0_C8_C7_0, x8
+// CHECK-ASM: sys #0, c8, c7, #0, x8
// CHECK-IR: %[[VAR:.*]] = load i64,
-// CHECK-IR-NEXT: call void @llvm.write_register.i64(metadata ![[MD5:.*]], i64 %[[VAR]])
+// CHECK-IR-NEXT: call void @llvm.aarch64.sys(i32 0, i32 8, i32 7, i32 0, i64 %[[VAR]])
__sys(ARM64_CFP_RCTX, v);
-// CHECK-ASM: msr S1_3_C7_C3_4, x8
+// CHECK-ASM: sys #3, c7, c3, #4, x8
// CHECK-IR: %[[VAR:.*]] = load i64,
-// CHECK-IR-NEXT: call void @llvm.write_register.i64(metadata ![[MD6:.*]], i64 %[[VAR]])
+// CHECK-IR-NEXT: call void @llvm.aarch64.sys(i32 3, i32 7, i32 3, i32 4, i64 %[[VAR]])
}
-
-// CHECK-IR: ![[MD2]] = !{!"1:0:7:10:6"}
-// CHECK-IR: ![[MD3]] = !{!"1:0:7:5:0"}
-// CHECK-IR: ![[MD4]] = !{!"1:4:7:8:1"}
-// CHECK-IR: ![[MD5]] = !{!"1:0:8:7:0"}
-// CHECK-IR: ![[MD6]] = !{!"1:3:7:3:4"}
diff --git a/clang/test/Sema/builtins-microsoft-arm64.c b/clang/test/Sema/builtins-microsoft-arm64.c
index a915a370cde14..22163ab3fa851 100644
--- a/clang/test/Sema/builtins-microsoft-arm64.c
+++ b/clang/test/Sema/builtins-microsoft-arm64.c
@@ -25,11 +25,24 @@ void check_ReadWriteStatusReg(int v) {
_WriteStatusReg(x, v); // expected-error {{argument to '_WriteStatusReg' must be a constant integer}}
}
+void check_ReadWriteStatusReg_range(int v) {
+ _ReadStatusReg(0x3fff); // expected-error-re {{argument value {{.*}} is outside the valid range}}
+ _ReadStatusReg(0x8000); // expected-error-re {{argument value {{.*}} is outside the valid range}}
+
+ _WriteStatusReg(0x3fff, v); // expected-error-re {{argument value {{.*}} is outside the valid range}}
+ _WriteStatusReg(0x8000, v); // expected-error-re {{argument value {{.*}} is outside the valid range}}
+}
+
void check__sys(int v) {
int x;
__sys(x, v); // expected-error {{argument to '__sys' must be a constant integer}}
}
+void check__sys_range(int v) {
+ __sys(-1, v); // expected-error-re {{argument value {{.*}} is outside the valid range}}
+ __sys(0x4000, v); // expected-error-re {{argument value {{.*}} is outside the valid range}}
+}
+
unsigned int check__sys_retval() {
return __sys(0, 1); // builtin has superfluous return value for MSVC compatibility
}
diff --git a/llvm/include/llvm/IR/IntrinsicsAArch64.td b/llvm/include/llvm/IR/IntrinsicsAArch64.td
index 75929cbc222ad..63500beaa6521 100644
--- a/llvm/include/llvm/IR/IntrinsicsAArch64.td
+++ b/llvm/include/llvm/IR/IntrinsicsAArch64.td
@@ -57,7 +57,10 @@ def int_aarch64_frint64x
: DefaultAttrsIntrinsic<[ llvm_anyfloat_ty ], [ LLVMMatchType<0> ],
[ IntrNoMem ]>;
-
+def int_aarch64_sys : DefaultAttrsIntrinsic<[], [llvm_i32_ty, llvm_i32_ty,
+ llvm_i32_ty, llvm_i32_ty, llvm_i64_ty],
+ [ImmArg<ArgIndex<0>>, ImmArg<ArgIndex<1>>, ImmArg<ArgIndex<2>>,
+ ImmArg<ArgIndex<3>>]>;
//===----------------------------------------------------------------------===//
// HINT
diff --git a/llvm/lib/Target/AArch64/AArch64InstrFormats.td b/llvm/lib/Target/AArch64/AArch64InstrFormats.td
index 43727188a06d0..19312d34609ce 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrFormats.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrFormats.td
@@ -2116,16 +2116,21 @@ def SysCRAsmOperand : AsmOperandClass {
let ParserMethod = "tryParseSysCROperand";
}
-def sys_cr_op : Operand<i32> {
+def sys_cr_op : Operand<i32>, TImmLeaf<i32, [{
+ return ((uint32_t)Imm) < 16;
+}]> {
let PrintMethod = "printSysCROperand";
let ParserMatchClass = SysCRAsmOperand;
let OperandType = "OPERAND_IMMEDIATE";
}
-class SystemXtI<bit L, string asm>
- : RtSystemI<L, (outs),
- (ins imm0_7:$op1, sys_cr_op:$Cn, sys_cr_op:$Cm, imm0_7:$op2, GPR64:$Rt),
- asm, "\t$op1, $Cn, $Cm, $op2, $Rt"> {
+class SystemXtI<string asm>
+ : RtSystemI<0, (outs),
+ (ins imm32_0_7:$op1, sys_cr_op:$Cn, sys_cr_op:$Cm, imm32_0_7:$op2,
+ GPR64:$Rt),
+ asm, "\t$op1, $Cn, $Cm, $op2, $Rt",
+ [(int_aarch64_sys timm32_0_7:$op1, sys_cr_op:$Cn, sys_cr_op:$Cm,
+ timm32_0_7:$op2, GPR64:$Rt)]> {
bits<3> op1;
bits<4> Cn;
bits<4> Cm;
@@ -2135,6 +2140,10 @@ class SystemXtI<bit L, string asm>
let Inst{15-12} = Cn;
let Inst{11-8} = Cm;
let Inst{7-5} = op2;
+
+ let mayLoad = 1;
+ let mayStore = 1;
+ let hasSideEffects = 1;
}
class SystemLXtI<bit L, string asm>
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index 44968b14b11a9..2ecfdcf1508ac 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -2567,12 +2567,12 @@ def MSR_FPMR : Pseudo<(outs), (ins GPR64:$val),
Sched<[WriteSys]>;
// Generic system instructions
-def SYSxt : SystemXtI<0, "sys">;
+def SYSxt : SystemXtI<"sys">;
def SYSLxt : SystemLXtI<1, "sysl">;
def : InstAlias<"sys $op1, $Cn, $Cm, $op2",
- (SYSxt imm0_7:$op1, sys_cr_op:$Cn,
- sys_cr_op:$Cm, imm0_7:$op2, XZR)>;
+ (SYSxt timm32_0_7:$op1, sys_cr_op:$Cn,
+ sys_cr_op:$Cm, timm32_0_7:$op2, XZR)>;
//===----------------------------------------------------------------------===//
diff --git a/llvm/test/CodeGen/AArch64/aarch64-sys-intrinsic.ll b/llvm/test/CodeGen/AArch64/aarch64-sys-intrinsic.ll
new file mode 100644
index 0000000000000..79ee54a97e53a
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/aarch64-sys-intrinsic.ll
@@ -0,0 +1,126 @@
+; RUN: llc < %s -mtriple=aarch64 -asm-verbose=false | FileCheck %s --check-prefix=CHECK
+; RUN: llc < %s -mtriple=aarch64 -mattr=+predres -asm-verbose=false | FileCheck %s --check-prefix=PREDRES
+; RUN: llc < %s -mtriple=aarch64 -mattr=+specres2 -asm-verbose=false | FileCheck %s --check-prefix=SPECRES2
+; RUN: llc < %s -mtriple=aarch64 -mattr=+gcie -asm-verbose=false | FileCheck %s --check-prefix=GCIE
+; RUN: llc < %s -mtriple=aarch64 -mattr=+poe2 -asm-verbose=false | FileCheck %s --check-prefix=POE2
+; RUN: llc < %s -mtriple=aarch64 -mattr=+mpamv2 -asm-verbose=false | FileCheck %s --check-prefix=MPAMV2
+
+declare void @llvm.aarch64.sys(i32 immarg, i32 immarg, i32 immarg, i32 immarg,
+ i64)
+
+define void @sys_random(i64 %x) {
+; CHECK-LABEL: sys_random:
+; CHECK: sys #0, c7, c10, #6, x0
+; CHECK-NEXT: ret
+entry:
+ call void @llvm.aarch64.sys(i32 0, i32 7, i32 10, i32 6, i64 %x)
+ ret void
+}
+
+define void @sys_ic_iallu() {
+; CHECK-LABEL: sys_ic_iallu:
+; CHECK: ic iallu
+; CHECK-NEXT: ret
+entry:
+ call void @llvm.aarch64.sys(i32 0, i32 7, i32 5, i32 0, i64 0)
+ ret void
+}
+
+define void @sys_dc_cvac(i64 %x) {
+; CHECK-LABEL: sys_dc_cvac:
+; CHECK: dc cvac, x0
+; CHECK-NEXT: ret
+entry:
+ call void @llvm.aarch64.sys(i32 3, i32 7, i32 10, i32 1, i64 %x)
+ ret void
+}
+
+define void @sys_at_s1e2w(i64 %x) {
+; CHECK-LABEL: sys_at_s1e2w:
+; CHECK: at s1e2w, x0
+; CHECK-NEXT: ret
+entry:
+ call void @llvm.aarch64.sys(i32 4, i32 7, i32 8, i32 1, i64 %x)
+ ret void
+}
+
+define void @sys_tlbi_vmalle1() {
+; CHECK-LABEL: sys_tlbi_vmalle1:
+; CHECK: tlbi vmalle1
+; CHECK-NEXT: ret
+entry:
+ call void @llvm.aarch64.sys(i32 0, i32 8, i32 7, i32 0, i64 0)
+ ret void
+}
+
+define void @sys_cfp_rctx(i64 %x) {
+; PREDRES-LABEL: sys_cfp_rctx:
+; PREDRES: cfp rctx, x0
+; PREDRES-NEXT: ret
+entry:
+ call void @llvm.aarch64.sys(i32 3, i32 7, i32 3, i32 4, i64 %x)
+ ret void
+}
+
+define void @sys_dvp_rctx(i64 %x) {
+; PREDRES-LABEL: sys_dvp_rctx:
+; PREDRES: dvp rctx, x0
+; PREDRES-NEXT: ret
+entry:
+ call void @llvm.aarch64.sys(i32 3, i32 7, i32 3, i32 5, i64 %x)
+ ret void
+}
+
+define void @sys_cpp_rctx(i64 %x) {
+; PREDRES-LABEL: sys_cpp_rctx:
+; PREDRES: cpp rctx, x0
+; PREDRES-NEXT: ret
+entry:
+ call void @llvm.aarch64.sys(i32 3, i32 7, i32 3, i32 7, i64 %x)
+ ret void
+}
+
+define void @sys_cosp_rctx(i64 %x) {
+; SPECRES2-LABEL: sys_cosp_rctx:
+; SPECRES2: cosp rctx, x0
+; SPECRES2-NEXT: ret
+entry:
+ call void @llvm.aarch64.sys(i32 3, i32 7, i32 3, i32 6, i64 %x)
+ ret void
+}
+
+define void @sys_gic_cdaff(i64 %x) {
+; GCIE-LABEL: sys_gic_cdaff:
+; GCIE: gic cdaff, x0
+; GCIE-NEXT: ret
+entry:
+ call void @llvm.aarch64.sys(i32 0, i32 12, i32 1, i32 3, i64 %x)
+ ret void
+}
+
+define void @sys_gsb_sys() {
+; GCIE-LABEL: sys_gsb_sys:
+; GCIE: gsb sys
+; GCIE-NEXT: ret
+entry:
+ call void @llvm.aarch64.sys(i32 0, i32 12, i32 0, i32 0, i64 0)
+ ret void
+}
+
+define void @sys_plbi_vmalle1() {
+; POE2-LABEL: sys_plbi_vmalle1:
+; POE2: plbi vmalle1
+; POE2-NEXT: ret
+entry:
+ call void @llvm.aarch64.sys(i32 0, i32 10, i32 7, i32 0, i64 0)
+ ret void
+}
+
+define void @sys_mlbi_vmalle1() {
+; MPAMV2-LABEL: sys_mlbi_vmalle1:
+; MPAMV2: mlbi vmalle1
+; MPAMV2-NEXT: ret
+entry:
+ call void @llvm.aarch64.sys(i32 4, i32 7, i32 0, i32 5, i64 0)
+ ret void
+}
diff --git a/llvm/test/tools/llvm-mca/AArch64/Neoverse/V1-misc-instructions.s b/llvm/test/tools/llvm-mca/AArch64/Neoverse/V1-misc-instructions.s
index 53ade6eeb77a8..37975ab269d10 100644
--- a/llvm/test/tools/llvm-mca/AArch64/Neoverse/V1-misc-instructions.s
+++ b/llvm/test/tools/llvm-mca/AArch64/Neoverse/V1-misc-instructions.s
@@ -30,7 +30,7 @@ sysl x16, #5, c11, c8, #5
# CHECK-NEXT: [6]: HasSideEffects (U)
# CHECK: [1] [2] [3] [4] [5] [6] Instructions:
-# CHECK-NEXT: 1 1 0.13 U at s12e1r, x28
+# CHECK-NEXT: 1 1 0.13 * * U at s12e1r, x28
# CHECK-NEXT: 1 1 0.13 U brk #0x8415
# CHECK-NEXT: 1 1 0.13 * * U clrex
# CHECK-NEXT: 1 1 0.13 * * U csdb
More information about the cfe-commits
mailing list