MipsDelaySlotFiller: Don't move BUNDLE instructions into the delay slot

Summary:
In our CHERI fork we use BUNDLE instructions to ensure that a
three-instruction sequence to generate a program-counter-relative value is
emitted without reordering or insertions (since that would break the 32-bit
offset computation). This sequence is created in MipsExpandPseudo and we use
finalizeBundle() to create the BUNDLE instruction.

However, the delay slot filler currently breaks this pattern since the BUNDLE
will be removed and so all instructions are moved into the delay slot.
Since the delay slot only executes the first instruction, this results in
incorrect computations (and run-time crashes) if the branch is taken.

The original test cases uses CHERI instructions, so for the test case here
I simple filled a BUNDLE with a no-op DADDiu $sp_64, -16 and DADDiu $sp_64, 16.

Reviewers: atanasyan

Reviewed By: atanasyan

Subscribers: merge_guards_bot, sdardis, hiraditya, jrtc27, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D70944
This commit is contained in:
Alex Richardson 2019-12-04 10:06:02 +00:00
parent 0cc4b95985
commit b91f239485
2 changed files with 148 additions and 0 deletions

View File

@ -695,6 +695,12 @@ bool MipsDelaySlotFiller::searchRange(MachineBasicBlock &MBB, IterTy Begin,
continue;
}
if (CurrI->isBundle()) {
LLVM_DEBUG(dbgs() << DEBUG_TYPE ": ignoring BUNDLE instruction: ";
CurrI->dump());
continue;
}
if (terminateSearch(*CurrI)) {
LLVM_DEBUG(dbgs() << DEBUG_TYPE ": should terminate search: ";
CurrI->dump());

View File

@ -0,0 +1,142 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
## Check that the delay-slot filler does not attempt to split BUNDLE instructions
# RUN: llc %s -start-before=mips-delay-slot-filler -stop-after=mips-delay-slot-filler \
# RUN: -verify-machineinstrs -o - | FileCheck %s
--- |
target datalayout = "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128"
target triple = "mips64-unknown-freebsd"
declare i8* @func_a(i64 zeroext)
declare i8* @func_b(i64 zeroext)
; Function Attrs: nounwind
define i8* @test(i64 zeroext %nbytes) local_unnamed_addr #0 {
entry:
%cmp = icmp eq i64 %nbytes, 0
br i1 %cmp, label %if.else, label %if.then
if.then: ; preds = %entry
%call = tail call i8* @func_a(i64 zeroext %nbytes)
br label %return
if.else: ; preds = %entry
%call1 = tail call i8* @func_b(i64 zeroext 0)
br label %return
return: ; preds = %if.else, %if.then
%retval.0 = phi i8* [ %call, %if.then ], [ %call1, %if.else ]
ret i8* %retval.0
}
; Function Attrs: nounwind
declare void @llvm.stackprotector(i8*, i8**) #0
attributes #0 = { nounwind }
...
---
name: test
alignment: 8
exposesReturnsTwice: false
legalized: false
regBankSelected: false
selected: false
failedISel: false
tracksRegLiveness: true
hasWinCFI: false
registers: []
liveins:
- { reg: '$a0_64', virtual-reg: '' }
frameInfo:
isFrameAddressTaken: false
isReturnAddressTaken: false
hasStackMap: false
hasPatchPoint: false
stackSize: 16
offsetAdjustment: 0
maxAlignment: 8
adjustsStack: true
hasCalls: true
stackProtector: ''
maxCallFrameSize: 0
cvBytesOfCalleeSavedRegisters: 0
hasOpaqueSPAdjustment: false
hasVAStart: false
hasMustTailInVarArgFunc: false
localFrameSize: 0
savePoint: ''
restorePoint: ''
fixedStack: []
stack:
- { id: 0, name: '', type: spill-slot, offset: -8, size: 8, alignment: 8,
stack-id: default, callee-saved-register: '$ra_64', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
callSites: []
constants: []
machineFunctionInfo: {}
body: |
; CHECK-LABEL: name: test
; CHECK: bb.0.entry:
; CHECK: successors: %bb.2(0x30000000), %bb.1(0x50000000)
; CHECK: $sp_64 = DADDiu $sp_64, -16
; CHECK: CFI_INSTRUCTION def_cfa_offset 16
; CHECK: SD killed $ra_64, $sp_64, 8 :: (store 8 into %stack.0)
; CHECK: CFI_INSTRUCTION offset $ra_64, -8
; CHECK: BUNDLE {
; CHECK: $sp_64 = DADDiu $sp_64, -16
; CHECK: $sp_64 = DADDiu $sp_64, 16
; CHECK: }
; CHECK: BEQ64 renamable $a0_64, $zero_64, %bb.2, implicit-def $at {
; CHECK: NOP
; CHECK: }
; CHECK: bb.1.if.then:
; CHECK: successors: %bb.3(0x80000000)
; CHECK: JAL @func_a, csr_n64, implicit-def dead $ra, implicit $a0_64, implicit-def $sp, implicit-def $v0_64 {
; CHECK: NOP
; CHECK: }
; CHECK: J %bb.3, implicit-def dead $at {
; CHECK: NOP
; CHECK: }
; CHECK: bb.2.if.else:
; CHECK: successors: %bb.3(0x80000000)
; CHECK: JAL @func_b, csr_n64, implicit-def dead $ra, implicit $a0_64, implicit-def $sp, implicit-def $v0_64 {
; CHECK: $a0_64 = DADDiu $zero_64, 0
; CHECK: }
; CHECK: bb.3.return:
; CHECK: $ra_64 = LD $sp_64, 8 :: (load 8 from %stack.0)
; CHECK: PseudoReturn64 undef $ra_64, implicit $v0_64 {
; CHECK: $sp_64 = DADDiu $sp_64, 16
; CHECK: }
bb.0.entry:
successors: %bb.2(0x30000000), %bb.1(0x50000000)
liveins: $a0_64, $ra_64
$sp_64 = DADDiu $sp_64, -16
CFI_INSTRUCTION def_cfa_offset 16
SD killed $ra_64, $sp_64, 8 :: (store 8 into %stack.0)
CFI_INSTRUCTION offset $ra_64, -8
; This BUNDLE instruction must not be split by the delay slot filler:
BUNDLE {
$sp_64 = DADDiu $sp_64, -16
$sp_64 = DADDiu $sp_64, 16
}
BEQ64 renamable $a0_64, $zero_64, %bb.2, implicit-def $at
bb.1.if.then:
successors: %bb.3(0x80000000)
liveins: $a0_64
JAL @func_a, csr_n64, implicit-def dead $ra, implicit $a0_64, implicit-def $sp, implicit-def $v0_64
J %bb.3, implicit-def dead $at
bb.2.if.else:
successors: %bb.3(0x80000000)
$a0_64 = DADDiu $zero_64, 0
JAL @func_b, csr_n64, implicit-def dead $ra, implicit $a0_64, implicit-def $sp, implicit-def $v0_64
bb.3.return:
liveins: $v0_64
$ra_64 = LD $sp_64, 8 :: (load 8 from %stack.0)
$sp_64 = DADDiu $sp_64, 16
PseudoReturn64 undef $ra_64, implicit $v0_64
...