Skip to content

Commit 5165b2b

Browse files
committed
AArch64+ARM: make LLVM consider system registers volatile.
Some of the system registers readable on AArch64 and ARM platforms return different values with each read (for example a timer counter), these shouldn't be hoisted outside loops or otherwise interfered with, but the normal @llvm.read_register intrinsic is only considered to read memory. This introduces a separate @llvm.read_volatile_register intrinsic and maps all system-registers on ARM platforms to use it for the __builtin_arm_rsr calls. Registers declared with asm("r9") or similar are unaffected.
1 parent fe59122 commit 5165b2b

File tree

8 files changed

+86
-27
lines changed

8 files changed

+86
-27
lines changed

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6361,14 +6361,20 @@ Value *CodeGenFunction::GetValueForARMHint(unsigned BuiltinID) {
63616361
llvm::ConstantInt::get(Int32Ty, Value));
63626362
}
63636363

6364+
enum SpecialRegisterAccessKind {
6365+
NormalRead,
6366+
VolatileRead,
6367+
Write,
6368+
};
6369+
63646370
// Generates the IR for the read/write special register builtin,
63656371
// ValueType is the type of the value that is to be written or read,
63666372
// RegisterType is the type of the register being written to or read from.
63676373
static Value *EmitSpecialRegisterBuiltin(CodeGenFunction &CGF,
63686374
const CallExpr *E,
63696375
llvm::Type *RegisterType,
63706376
llvm::Type *ValueType,
6371-
bool IsRead,
6377+
SpecialRegisterAccessKind AccessKind,
63726378
StringRef SysReg = "") {
63736379
// write and register intrinsics only support 32 and 64 bit operations.
63746380
assert((RegisterType->isIntegerTy(32) || RegisterType->isIntegerTy(64))
@@ -6393,8 +6399,12 @@ static Value *EmitSpecialRegisterBuiltin(CodeGenFunction &CGF,
63936399
assert(!(RegisterType->isIntegerTy(32) && ValueType->isIntegerTy(64))
63946400
&& "Can't fit 64-bit value in 32-bit register");
63956401

6396-
if (IsRead) {
6397-
llvm::Function *F = CGM.getIntrinsic(llvm::Intrinsic::read_register, Types);
6402+
if (AccessKind != Write) {
6403+
assert(AccesKind == NormalRead || AccessKind == VolatileRead);
6404+
llvm::Function *F = CGM.getIntrinsic(
6405+
AccessKind == VolatileRead ? llvm::Intrinsic::read_volatile_register
6406+
: llvm::Intrinsic::read_register,
6407+
Types);
63986408
llvm::Value *Call = Builder.CreateCall(F, Metadata);
63996409

64006410
if (MixedTypes)
@@ -6773,9 +6783,11 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID,
67736783
BuiltinID == ARM::BI__builtin_arm_wsr64 ||
67746784
BuiltinID == ARM::BI__builtin_arm_wsrp) {
67756785

6776-
bool IsRead = BuiltinID == ARM::BI__builtin_arm_rsr ||
6777-
BuiltinID == ARM::BI__builtin_arm_rsr64 ||
6778-
BuiltinID == ARM::BI__builtin_arm_rsrp;
6786+
SpecialRegisterAccessKind AccessKind = Write;
6787+
if (BuiltinID == ARM::BI__builtin_arm_rsr ||
6788+
BuiltinID == ARM::BI__builtin_arm_rsr64 ||
6789+
BuiltinID == ARM::BI__builtin_arm_rsrp)
6790+
AccessKind = VolatileRead;
67796791

67806792
bool IsPointerBuiltin = BuiltinID == ARM::BI__builtin_arm_rsrp ||
67816793
BuiltinID == ARM::BI__builtin_arm_wsrp;
@@ -6794,7 +6806,8 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID,
67946806
ValueType = RegisterType = Int32Ty;
67956807
}
67966808

6797-
return EmitSpecialRegisterBuiltin(*this, E, RegisterType, ValueType, IsRead);
6809+
return EmitSpecialRegisterBuiltin(*this, E, RegisterType, ValueType,
6810+
AccessKind);
67986811
}
67996812

68006813
// Deal with MVE builtins
@@ -8834,9 +8847,11 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
88348847
BuiltinID == AArch64::BI__builtin_arm_wsr64 ||
88358848
BuiltinID == AArch64::BI__builtin_arm_wsrp) {
88368849

8837-
bool IsRead = BuiltinID == AArch64::BI__builtin_arm_rsr ||
8838-
BuiltinID == AArch64::BI__builtin_arm_rsr64 ||
8839-
BuiltinID == AArch64::BI__builtin_arm_rsrp;
8850+
SpecialRegisterAccessKind AccessKind = Write;
8851+
if (BuiltinID == AArch64::BI__builtin_arm_rsr ||
8852+
BuiltinID == AArch64::BI__builtin_arm_rsr64 ||
8853+
BuiltinID == AArch64::BI__builtin_arm_rsrp)
8854+
AccessKind = VolatileRead;
88408855

88418856
bool IsPointerBuiltin = BuiltinID == AArch64::BI__builtin_arm_rsrp ||
88428857
BuiltinID == AArch64::BI__builtin_arm_wsrp;
@@ -8854,7 +8869,8 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
88548869
ValueType = Int32Ty;
88558870
}
88568871

8857-
return EmitSpecialRegisterBuiltin(*this, E, RegisterType, ValueType, IsRead);
8872+
return EmitSpecialRegisterBuiltin(*this, E, RegisterType, ValueType,
8873+
AccessKind);
88588874
}
88598875

88608876
if (BuiltinID == AArch64::BI_ReadStatusReg ||
@@ -14797,7 +14813,7 @@ Value *CodeGenFunction::EmitAMDGPUBuiltinExpr(unsigned BuiltinID,
1479714813
}
1479814814
case AMDGPU::BI__builtin_amdgcn_read_exec: {
1479914815
CallInst *CI = cast<CallInst>(
14800-
EmitSpecialRegisterBuiltin(*this, E, Int64Ty, Int64Ty, true, "exec"));
14816+
EmitSpecialRegisterBuiltin(*this, E, Int64Ty, Int64Ty, NormalRead, "exec"));
1480114817
CI->setConvergent();
1480214818
return CI;
1480314819
}
@@ -14806,7 +14822,7 @@ Value *CodeGenFunction::EmitAMDGPUBuiltinExpr(unsigned BuiltinID,
1480614822
StringRef RegName = BuiltinID == AMDGPU::BI__builtin_amdgcn_read_exec_lo ?
1480714823
"exec_lo" : "exec_hi";
1480814824
CallInst *CI = cast<CallInst>(
14809-
EmitSpecialRegisterBuiltin(*this, E, Int32Ty, Int32Ty, true, RegName));
14825+
EmitSpecialRegisterBuiltin(*this, E, Int32Ty, Int32Ty, NormalRead, RegName));
1481014826
CI->setConvergent();
1481114827
return CI;
1481214828
}

clang/test/CodeGen/builtins-arm.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,19 +222,19 @@ uint64_t mrrc2() {
222222
}
223223

224224
unsigned rsr() {
225-
// CHECK: [[V0:[%A-Za-z0-9.]+]] = call i32 @llvm.read_register.i32(metadata ![[M0:.*]])
225+
// CHECK: [[V0:[%A-Za-z0-9.]+]] = call i32 @llvm.read_volatile_register.i32(metadata ![[M0:.*]])
226226
// CHECK-NEXT: ret i32 [[V0]]
227227
return __builtin_arm_rsr("cp1:2:c3:c4:5");
228228
}
229229

230230
unsigned long long rsr64() {
231-
// CHECK: [[V0:[%A-Za-z0-9.]+]] = call i64 @llvm.read_register.i64(metadata ![[M1:.*]])
231+
// CHECK: [[V0:[%A-Za-z0-9.]+]] = call i64 @llvm.read_volatile_register.i64(metadata ![[M1:.*]])
232232
// CHECK-NEXT: ret i64 [[V0]]
233233
return __builtin_arm_rsr64("cp1:2:c3");
234234
}
235235

236236
void *rsrp() {
237-
// CHECK: [[V0:[%A-Za-z0-9.]+]] = call i32 @llvm.read_register.i32(metadata ![[M2:.*]])
237+
// CHECK: [[V0:[%A-Za-z0-9.]+]] = call i32 @llvm.read_volatile_register.i32(metadata ![[M2:.*]])
238238
// CHECK-NEXT: [[V1:[%A-Za-z0-9.]+]] = inttoptr i32 [[V0]] to i8*
239239
// CHECK-NEXT: ret i8* [[V1]]
240240
return __builtin_arm_rsrp("sysreg");

clang/test/CodeGen/builtins-arm64.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,20 +68,20 @@ int32_t jcvt(double v) {
6868
__typeof__(__builtin_arm_rsr("1:2:3:4:5")) rsr(void);
6969

7070
uint32_t rsr() {
71-
// CHECK: [[V0:[%A-Za-z0-9.]+]] = call i64 @llvm.read_register.i64(metadata ![[M0:[0-9]]])
71+
// CHECK: [[V0:[%A-Za-z0-9.]+]] = call i64 @llvm.read_volatile_register.i64(metadata ![[M0:[0-9]]])
7272
// CHECK-NEXT: trunc i64 [[V0]] to i32
7373
return __builtin_arm_rsr("1:2:3:4:5");
7474
}
7575

7676
__typeof__(__builtin_arm_rsr64("1:2:3:4:5")) rsr64(void);
7777

7878
uint64_t rsr64(void) {
79-
// CHECK: call i64 @llvm.read_register.i64(metadata ![[M0:[0-9]]])
79+
// CHECK: call i64 @llvm.read_volatile_register.i64(metadata ![[M0:[0-9]]])
8080
return __builtin_arm_rsr64("1:2:3:4:5");
8181
}
8282

8383
void *rsrp() {
84-
// CHECK: [[V0:[%A-Za-z0-9.]+]] = call i64 @llvm.read_register.i64(metadata ![[M0:[0-9]]])
84+
// CHECK: [[V0:[%A-Za-z0-9.]+]] = call i64 @llvm.read_volatile_register.i64(metadata ![[M0:[0-9]]])
8585
// CHECK-NEXT: inttoptr i64 [[V0]] to i8*
8686
return __builtin_arm_rsrp("1:2:3:4:5");
8787
}

llvm/docs/LangRef.rst

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11643,9 +11643,11 @@ the escaped allocas are allocated, which would break attempts to use
1164311643
'``llvm.localrecover``'.
1164411644

1164511645
.. _int_read_register:
11646+
.. _int_read_volatile_register:
1164611647
.. _int_write_register:
1164711648

11648-
'``llvm.read_register``' and '``llvm.write_register``' Intrinsics
11649+
'``llvm.read_register``', '``llvm.read_volatile_register``', and
11650+
'``llvm.write_register``' Intrinsics
1164911651
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1165011652

1165111653
Syntax:
@@ -11655,24 +11657,30 @@ Syntax:
1165511657

1165611658
declare i32 @llvm.read_register.i32(metadata)
1165711659
declare i64 @llvm.read_register.i64(metadata)
11660+
declare i32 @llvm.read_volatile_register.i32(metadata)
11661+
declare i64 @llvm.read_volatile_register.i64(metadata)
1165811662
declare void @llvm.write_register.i32(metadata, i32 @value)
1165911663
declare void @llvm.write_register.i64(metadata, i64 @value)
1166011664
!0 = !{!"sp\00"}
1166111665

1166211666
Overview:
1166311667
"""""""""
1166411668

11665-
The '``llvm.read_register``' and '``llvm.write_register``' intrinsics
11666-
provides access to the named register. The register must be valid on
11667-
the architecture being compiled to. The type needs to be compatible
11668-
with the register being read.
11669+
The '``llvm.read_register``', '``llvm.read_volatile_register``', and
11670+
'``llvm.write_register``' intrinsics provide access to the named register.
11671+
The register must be valid on the architecture being compiled to. The type
11672+
needs to be compatible with the register being read.
1166911673

1167011674
Semantics:
1167111675
""""""""""
1167211676

11673-
The '``llvm.read_register``' intrinsic returns the current value of the
11674-
register, where possible. The '``llvm.write_register``' intrinsic sets
11675-
the current value of the register, where possible.
11677+
The '``llvm.read_register``' and '``llvm.read_volatile_register``' intrinsics
11678+
return the current value of the register, where possible. The
11679+
'``llvm.write_register``' intrinsic sets the current value of the register,
11680+
where possible.
11681+
11682+
A call to '``llvm.read_volatile_register``' is assumed to have side-effects
11683+
and possibly return a different value each time (e.g. for a timer register).
1167611684

1167711685
This is useful to implement named register global variables that need
1167811686
to always be mapped to a specific register, as is common practice on

llvm/include/llvm/IR/Intrinsics.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,9 @@ def int_read_register : Intrinsic<[llvm_anyint_ty], [llvm_metadata_ty],
458458
[IntrReadMem], "llvm.read_register">;
459459
def int_write_register : Intrinsic<[], [llvm_metadata_ty, llvm_anyint_ty],
460460
[], "llvm.write_register">;
461+
def int_read_volatile_register : Intrinsic<[llvm_anyint_ty], [llvm_metadata_ty],
462+
[IntrHasSideEffects],
463+
"llvm.read_volatile_register">;
461464

462465
// Gets the address of the local variable area. This is typically a copy of the
463466
// stack, frame, or base pointer depending on the type of prologue.

llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1598,6 +1598,7 @@ bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID,
15981598
case Intrinsic::sideeffect:
15991599
// Discard annotate attributes, assumptions, and artificial side-effects.
16001600
return true;
1601+
case Intrinsic::read_volatile_register:
16011602
case Intrinsic::read_register: {
16021603
Value *Arg = CI.getArgOperand(0);
16031604
MIRBuilder

llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5698,6 +5698,7 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
56985698
TLI.getFrameIndexTy(DAG.getDataLayout()),
56995699
getValue(I.getArgOperand(0))));
57005700
return;
5701+
case Intrinsic::read_volatile_register:
57015702
case Intrinsic::read_register: {
57025703
Value *Reg = I.getArgOperand(0);
57035704
SDValue Chain = getRoot();
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
; RUN: opt -S -licm %s | FileCheck %s
2+
3+
; Volatile register shouldn't be hoisted ourside loops.
4+
define i32 @test_read() {
5+
; CHECK-LABEL: define i32 @test_read()
6+
; CHECK: br label %loop
7+
; CHECK: loop:
8+
; CHECK: %counter = tail call i64 @llvm.read_volatile_register
9+
10+
entry:
11+
br label %loop
12+
13+
loop:
14+
%i = phi i32 [ 0, %entry ], [ %i.next, %inc ]
15+
%counter = tail call i64 @llvm.read_volatile_register.i64(metadata !1)
16+
%tst = icmp ult i64 %counter, 1000
17+
br i1 %tst, label %inc, label %done
18+
19+
inc:
20+
%i.next = add nuw nsw i32 %i, 1
21+
br label %loop
22+
23+
done:
24+
ret i32 %i
25+
}
26+
27+
declare i64 @llvm.read_register.i64(metadata)
28+
declare i64 @llvm.read_volatile_register.i64(metadata)
29+
30+
!1 = !{!"cntpct_el0"}

0 commit comments

Comments
 (0)