[PATCH] D49364: [ARM] Add support for spilling high registers in Thumb1

Petr Pavlu via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 16 01:40:25 PDT 2018


petpav01 created this revision.
petpav01 added reviewers: olista01, t.p.northover.
Herald added subscribers: llvm-commits, chrib, kristof.beyls, qcolombet.
Herald added a reviewer: javed.absar.

LLVM normally only makes use of low registers in Thumb1 and methods `Thumb1InstrInfo::storeRegToStackSlot()/loadRegFromStackSlot()` are currently able to store/restore only them. However, it is possible in rare cases that a register allocator might need to spill a high register in the middle of a function as well.

Example:

  $ cat test.c
  void constraint_h(void) {
    int i;
    asm volatile("@ %0" : : "h" (i) : "r12");
  }
  $ clang -target arm-none-eabi -march=armv6-m -c test.c
  clang-7: [...]/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp:85: virtual void llvm::Thumb1InstrInfo::storeRegToStackSlot(llvm::MachineBasicBlock&, llvm::MachineBasicBlock::iterator, unsigned int, bool, int, const llvm::TargetRegisterClass*, const llvm::TargetRegisterInfo*) const: Assertion `(RC == &ARM::tGPRRegClass || (TargetRegisterInfo::isPhysicalRegister(SrcReg) && isARMLowRegister(SrcReg))) && "Unknown regclass!"' failed.
  [...]

The program was compiled at `-O0` and so Fast Register Allocator is used. The following happens in this case:

- Prior to register allocation, MIR looks as follows:

  Frame Objects:
    fi#0: size=4, align=4, at location [SP]
  
  bb.0.entry:
    %1:tgpr = tLDRspi %stack.0.i, 0, 14, $noreg :: (dereferenceable load 4 from %ir.i)
    %0:hgpr = COPY %1:tgpr
    INLINEASM &"@ $0" [sideeffect] [attdialect], $0:[reguse:hGPR], %0:hgpr, $1:[clobber], implicit-def early-clobber $r12, !3
    tBX_RET 14, $noreg

- Fast Register Allocator first satisfies `%0:hgpr` by selecting r12.
- When the scan reaches the `INLINEASM` instruction, the allocator however notices that r12 is clobbered and so it needs to be spilled.
- The allocator calls `Thumb1InstrInfo::storeRegToStackSlot()` to store the register in a stack slot but the method does not know how to do it and aborts. This can also result in a miscompilation if LLVM is built without assertions enabled.

The patch extends `Thumb1InstrInfo::storeRegToStackSlot()` and `loadRegFromStackSlot()` to allow storing and restoring high registers. Since callers of these methods may expect only a single instruction to be added but Thumb1 does not have an instruction that directly stores a high register, the code in each method inserts a pseudo instruction that gets lowered after register allocation in `ThumbRegisterInfo::eliminateFrameIndex()`.

Note: Both the store and load need an additional low register. For instance, the store is implemented as:

  mov %lowReg, %spilledHighReg
  str %lowReg, ...

This relies on the register scavenger to secure some low register for the sequence. This is possibly problematic when the register pressure is high because `ThumbRegisterInfo::saveScavengerRegister()` currently also tries to make use of high register r12.


Repository:
  rL LLVM

https://reviews.llvm.org/D49364

Files:
  lib/Target/ARM/ARMInstrThumb.td
  lib/Target/ARM/ARMRegisterInfo.td
  lib/Target/ARM/Thumb1InstrInfo.cpp
  lib/Target/ARM/ThumbRegisterInfo.cpp
  lib/Target/ARM/ThumbRegisterInfo.h
  test/CodeGen/Thumb/high-reg-spill-expand.mir
  test/CodeGen/Thumb/high-reg-spill.mir

-------------- next part --------------
A non-text attachment was scrubbed...
Name: D49364.155628.patch
Type: text/x-patch
Size: 15944 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20180716/eb75f4f3/attachment.bin>


More information about the llvm-commits mailing list