Skip to content

Commit d85b387

Browse files
committed
[CodeGen][ARM] Error when writing to specific reserved registers in inline asm
Summary: No error or warning is emitted when specific reserved registers are written to in inline assembly. Therefore, writes to the program counter or to the frame pointer, for instance, were permitted, which could have led to undesirable behaviour. Example: int foo() { register int a __asm__("r7"); // r7 = frame-pointer in M-class ARM __asm__ __volatile__("mov %0, r1" : "=r"(a) : : ); return a; } In contrast, GCC issues an error in the same scenario. This patch detects writes to specific reserved registers in inline assembly for ARM and emits an error in such case. The detection works for output and input operands. Clobber operands are not handled here: they are already covered at a later point in AsmPrinter::emitInlineAsm(const MachineInstr *MI). The registers covered are: program counter, frame pointer and base pointer. This is ARM only. Therefore the implementation of other targets' counterparts remain open to do. Reviewers: efriedma Reviewed By: efriedma Subscribers: kristof.beyls, hiraditya, danielkiss, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D76848
1 parent 01bcc3e commit d85b387

File tree

5 files changed

+89
-0
lines changed

5 files changed

+89
-0
lines changed

llvm/include/llvm/CodeGen/TargetRegisterInfo.h

+6
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,12 @@ class TargetRegisterInfo : public MCRegisterInfo {
489489
return true;
490490
}
491491

492+
/// Returns true if PhysReg cannot be written to in inline asm statements.
493+
virtual bool isInlineAsmReadOnlyReg(const MachineFunction &MF,
494+
unsigned PhysReg) const {
495+
return false;
496+
}
497+
492498
/// Returns true if PhysReg is unallocatable and constant throughout the
493499
/// function. Used by MachineRegisterInfo::isConstantPhysReg().
494500
virtual bool isConstantPhysReg(MCRegister PhysReg) const { return false; }

llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -8168,6 +8168,21 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call) {
81688168
: OpInfo;
81698169
GetRegistersForValue(DAG, getCurSDLoc(), OpInfo, RefOpInfo);
81708170

8171+
auto DetectWriteToReservedRegister = [&]() {
8172+
const MachineFunction &MF = DAG.getMachineFunction();
8173+
const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
8174+
for (unsigned Reg : OpInfo.AssignedRegs.Regs) {
8175+
if (Register::isPhysicalRegister(Reg) &&
8176+
TRI.isInlineAsmReadOnlyReg(MF, Reg)) {
8177+
const char *RegName = TRI.getName(Reg);
8178+
emitInlineAsmError(CS, "write to reserved register '" +
8179+
Twine(RegName) + "'");
8180+
return true;
8181+
}
8182+
}
8183+
return false;
8184+
};
8185+
81718186
switch (OpInfo.Type) {
81728187
case InlineAsm::isOutput:
81738188
if (OpInfo.ConstraintType == TargetLowering::C_Memory) {
@@ -8193,6 +8208,9 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call) {
81938208
return;
81948209
}
81958210

8211+
if (DetectWriteToReservedRegister())
8212+
return;
8213+
81968214
// Add information to the INLINEASM node to know that this register is
81978215
// set.
81988216
OpInfo.AssignedRegs.AddInlineAsmOperands(
@@ -8339,6 +8357,9 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call) {
83398357
return;
83408358
}
83418359

8360+
if (DetectWriteToReservedRegister())
8361+
return;
8362+
83428363
SDLoc dl = getCurSDLoc();
83438364

83448365
OpInfo.AssignedRegs.getCopyToRegs(InOperandVal, DAG, dl, Chain, &Flag,

llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,21 @@ isAsmClobberable(const MachineFunction &MF, MCRegister PhysReg) const {
224224
return !getReservedRegs(MF).test(PhysReg);
225225
}
226226

227+
bool ARMBaseRegisterInfo::isInlineAsmReadOnlyReg(const MachineFunction &MF,
228+
unsigned PhysReg) const {
229+
const ARMSubtarget &STI = MF.getSubtarget<ARMSubtarget>();
230+
const ARMFrameLowering *TFI = getFrameLowering(MF);
231+
232+
BitVector Reserved(getNumRegs());
233+
markSuperRegs(Reserved, ARM::PC);
234+
if (TFI->hasFP(MF))
235+
markSuperRegs(Reserved, getFramePointerReg(STI));
236+
if (hasBasePointer(MF))
237+
markSuperRegs(Reserved, BasePtr);
238+
assert(checkAllSuperRegsMarked(Reserved));
239+
return Reserved.test(PhysReg);
240+
}
241+
227242
const TargetRegisterClass *
228243
ARMBaseRegisterInfo::getLargestLegalSuperClass(const TargetRegisterClass *RC,
229244
const MachineFunction &MF) const {

llvm/lib/Target/ARM/ARMBaseRegisterInfo.h

+2
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ class ARMBaseRegisterInfo : public ARMGenRegisterInfo {
135135
BitVector getReservedRegs(const MachineFunction &MF) const override;
136136
bool isAsmClobberable(const MachineFunction &MF,
137137
MCRegister PhysReg) const override;
138+
bool isInlineAsmReadOnlyReg(const MachineFunction &MF,
139+
unsigned PhysReg) const override;
138140

139141
const TargetRegisterClass *
140142
getPointerRegClass(const MachineFunction &MF,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
; RUN: not llc -mtriple thumbv6m-arm-none-eabi -frame-pointer=all %s 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR
2+
3+
; CHECK-ERROR: error: write to reserved register 'R7'
4+
define void @test_framepointer_output(i32 %input) {
5+
entry:
6+
%0 = call i32 asm sideeffect "mov $0, $1", "={r7},r"(i32 %input)
7+
ret void
8+
}
9+
10+
; CHECK-ERROR: error: write to reserved register 'R7'
11+
define void @test_framepointer_input(i32 %input) {
12+
entry:
13+
%0 = call i32 asm sideeffect "mov $0, $1", "=r,{r7}"(i32 %input)
14+
ret void
15+
}
16+
17+
; CHECK-ERROR: error: write to reserved register 'PC'
18+
define void @test_pc_output(i32 %input) {
19+
entry:
20+
%0 = call i32 asm sideeffect "mov $0, $1", "={pc},r"(i32 %input)
21+
ret void
22+
}
23+
24+
; CHECK-ERROR: error: write to reserved register 'PC'
25+
define void @test_pc_input(i32 %input) {
26+
entry:
27+
%0 = call i32 asm sideeffect "mov $0, $1", "=r,{pc}"(i32 %input)
28+
ret void
29+
}
30+
31+
; CHECK-ERROR: error: write to reserved register 'R6'
32+
define void @test_basepointer_output(i32 %size, i32 %input) alignstack(8) {
33+
entry:
34+
%vla = alloca i32, i32 %size, align 4
35+
%0 = call i32 asm sideeffect "mov $0, $1", "={r6},r"(i32 %input)
36+
ret void
37+
}
38+
39+
; CHECK-ERROR: error: write to reserved register 'R6'
40+
define void @test_basepointer_input(i32 %size, i32 %input) alignstack(8) {
41+
entry:
42+
%vla = alloca i32, i32 %size, align 4
43+
%0 = call i32 asm sideeffect "mov $0, $1", "=r,{r6}"(i32 %input)
44+
ret void
45+
}

0 commit comments

Comments
 (0)