Skip to content

Commit

Permalink
[CodeGen][ARM] Error when writing to specific reserved registers in i…
Browse files Browse the repository at this point in the history
…nline 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
  • Loading branch information
vhscampos committed Apr 15, 2020
1 parent 01bcc3e commit d85b387
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 0 deletions.
6 changes: 6 additions & 0 deletions llvm/include/llvm/CodeGen/TargetRegisterInfo.h
Expand Up @@ -489,6 +489,12 @@ class TargetRegisterInfo : public MCRegisterInfo {
return true;
}

/// Returns true if PhysReg cannot be written to in inline asm statements.
virtual bool isInlineAsmReadOnlyReg(const MachineFunction &MF,
unsigned PhysReg) const {
return false;
}

/// Returns true if PhysReg is unallocatable and constant throughout the
/// function. Used by MachineRegisterInfo::isConstantPhysReg().
virtual bool isConstantPhysReg(MCRegister PhysReg) const { return false; }
Expand Down
21 changes: 21 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
Expand Up @@ -8168,6 +8168,21 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call) {
: OpInfo;
GetRegistersForValue(DAG, getCurSDLoc(), OpInfo, RefOpInfo);

auto DetectWriteToReservedRegister = [&]() {
const MachineFunction &MF = DAG.getMachineFunction();
const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
for (unsigned Reg : OpInfo.AssignedRegs.Regs) {
if (Register::isPhysicalRegister(Reg) &&
TRI.isInlineAsmReadOnlyReg(MF, Reg)) {
const char *RegName = TRI.getName(Reg);
emitInlineAsmError(CS, "write to reserved register '" +
Twine(RegName) + "'");
return true;
}
}
return false;
};

switch (OpInfo.Type) {
case InlineAsm::isOutput:
if (OpInfo.ConstraintType == TargetLowering::C_Memory) {
Expand All @@ -8193,6 +8208,9 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call) {
return;
}

if (DetectWriteToReservedRegister())
return;

// Add information to the INLINEASM node to know that this register is
// set.
OpInfo.AssignedRegs.AddInlineAsmOperands(
Expand Down Expand Up @@ -8339,6 +8357,9 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call) {
return;
}

if (DetectWriteToReservedRegister())
return;

SDLoc dl = getCurSDLoc();

OpInfo.AssignedRegs.getCopyToRegs(InOperandVal, DAG, dl, Chain, &Flag,
Expand Down
15 changes: 15 additions & 0 deletions llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp
Expand Up @@ -224,6 +224,21 @@ isAsmClobberable(const MachineFunction &MF, MCRegister PhysReg) const {
return !getReservedRegs(MF).test(PhysReg);
}

bool ARMBaseRegisterInfo::isInlineAsmReadOnlyReg(const MachineFunction &MF,
unsigned PhysReg) const {
const ARMSubtarget &STI = MF.getSubtarget<ARMSubtarget>();
const ARMFrameLowering *TFI = getFrameLowering(MF);

BitVector Reserved(getNumRegs());
markSuperRegs(Reserved, ARM::PC);
if (TFI->hasFP(MF))
markSuperRegs(Reserved, getFramePointerReg(STI));
if (hasBasePointer(MF))
markSuperRegs(Reserved, BasePtr);
assert(checkAllSuperRegsMarked(Reserved));
return Reserved.test(PhysReg);
}

const TargetRegisterClass *
ARMBaseRegisterInfo::getLargestLegalSuperClass(const TargetRegisterClass *RC,
const MachineFunction &MF) const {
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/ARM/ARMBaseRegisterInfo.h
Expand Up @@ -135,6 +135,8 @@ class ARMBaseRegisterInfo : public ARMGenRegisterInfo {
BitVector getReservedRegs(const MachineFunction &MF) const override;
bool isAsmClobberable(const MachineFunction &MF,
MCRegister PhysReg) const override;
bool isInlineAsmReadOnlyReg(const MachineFunction &MF,
unsigned PhysReg) const override;

const TargetRegisterClass *
getPointerRegClass(const MachineFunction &MF,
Expand Down
45 changes: 45 additions & 0 deletions llvm/test/CodeGen/ARM/inline-asm-reserved-registers.ll
@@ -0,0 +1,45 @@
; RUN: not llc -mtriple thumbv6m-arm-none-eabi -frame-pointer=all %s 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR

; CHECK-ERROR: error: write to reserved register 'R7'
define void @test_framepointer_output(i32 %input) {
entry:
%0 = call i32 asm sideeffect "mov $0, $1", "={r7},r"(i32 %input)
ret void
}

; CHECK-ERROR: error: write to reserved register 'R7'
define void @test_framepointer_input(i32 %input) {
entry:
%0 = call i32 asm sideeffect "mov $0, $1", "=r,{r7}"(i32 %input)
ret void
}

; CHECK-ERROR: error: write to reserved register 'PC'
define void @test_pc_output(i32 %input) {
entry:
%0 = call i32 asm sideeffect "mov $0, $1", "={pc},r"(i32 %input)
ret void
}

; CHECK-ERROR: error: write to reserved register 'PC'
define void @test_pc_input(i32 %input) {
entry:
%0 = call i32 asm sideeffect "mov $0, $1", "=r,{pc}"(i32 %input)
ret void
}

; CHECK-ERROR: error: write to reserved register 'R6'
define void @test_basepointer_output(i32 %size, i32 %input) alignstack(8) {
entry:
%vla = alloca i32, i32 %size, align 4
%0 = call i32 asm sideeffect "mov $0, $1", "={r6},r"(i32 %input)
ret void
}

; CHECK-ERROR: error: write to reserved register 'R6'
define void @test_basepointer_input(i32 %size, i32 %input) alignstack(8) {
entry:
%vla = alloca i32, i32 %size, align 4
%0 = call i32 asm sideeffect "mov $0, $1", "=r,{r6}"(i32 %input)
ret void
}

0 comments on commit d85b387

Please sign in to comment.