llvm-project/llvm/test/CodeGen/AVR/interrupts.ll
Ayke van Laethem a560e57a7e
[AVR] Only push and clear R1 in interrupts when necessary
R1 is a reserved register, but LLVM gives the APIs to know when it is
used or not. So this patch uses these APIs to only save/clear/restore R1
in interrupts when necessary.

The main issue here was getting inline assembly to work. One could argue
that this is the job of Clang, but for consistency I've made sure that
R1 is always usable in inline assembly even if that means clearing it
when it might not be needed.

Information on inline assembly in AVR can be found here:

https://www.nongnu.org/avr-libc/user-manual/inline_asm.html#asm_code

Essentially, this seems to suggest that r1 can be freely used in avr-gcc
inline assembly, even without specifying it as an input operand.

Differential Revision: https://reviews.llvm.org/D117426
2022-08-15 14:29:38 +02:00

224 lines
5.0 KiB
LLVM

; RUN: llc < %s -march=avr | FileCheck %s
@count = global i8 0
@funcptr = global void () addrspace(1)* null
define avr_intrcc void @interrupt_handler() {
; CHECK-LABEL: interrupt_handler:
; CHECK: sei
; CHECK-NEXT: push r0
; CHECK-NEXT: in r0, 63
; CHECK-NEXT: push r0
; CHECK: pop r0
; CHECK-NEXT: out 63, r0
; CHECK-NEXT: pop r0
; CHECK-NEXT: reti
ret void
}
define void @interrupt_handler_via_ir_attribute() #0 {
; CHECK-LABEL: interrupt_handler_via_ir_attribute:
; CHECK: sei
; CHECK-NEXT: push r0
; CHECK-NEXT: in r0, 63
; CHECK-NEXT: push r0
; CHECK: pop r0
; CHECK-NEXT: out 63, r0
; CHECK-NEXT: pop r0
; CHECK-NEXT: reti
ret void
}
define avr_signalcc void @signal_handler() {
; CHECK-LABEL: signal_handler:
; CHECK-NOT: sei
; CHECK: push r0
; CHECK-NEXT: in r0, 63
; CHECK-NEXT: push r0
; CHECK: pop r0
; CHECK-NEXT: out 63, r0
; CHECK-NEXT: pop r0
; CHECK-NEXT: reti
ret void
}
define void @signal_handler_via_attribute() #1 {
; CHECK-LABEL: signal_handler_via_attribute:
; CHECK-NOT: sei
; CHECK: push r0
; CHECK-NEXT: in r0, 63
; CHECK-NEXT: push r0
; CHECK: pop r0
; CHECK-NEXT: out 63, r0
; CHECK-NEXT: pop r0
; CHECK-NEXT: reti
ret void
}
define avr_intrcc void @interrupt_alloca() {
; CHECK-LABEL: interrupt_alloca:
; CHECK: sei
; CHECK-NEXT: push r0
; CHECK-NEXT: in r0, 63
; CHECK-NEXT: push r0
; CHECK: push r28
; CHECK-NEXT: push r29
; CHECK-NEXT: in r28, 61
; CHECK-NEXT: in r29, 62
; CHECK-NEXT: sbiw r28, 1
; CHECK-NEXT: in r0, 63
; CHECK-NEXT: cli
; CHECK-NEXT: out 62, r29
; CHECK-NEXT: out 63, r0
; CHECK-NEXT: out 61, r28
; CHECK: adiw r28, 1
; CHECK-NEXT: in r0, 63
; CHECK-NEXT: cli
; CHECK-NEXT: out 62, r29
; CHECK-NEXT: out 63, r0
; CHECK-NEXT: out 61, r28
; CHECK-NEXT: pop r29
; CHECK-NEXT: pop r28
; CHECK: pop r0
; CHECK-NEXT: out 63, r0
; CHECK-NEXT: pop r0
; CHECK-NEXT: reti
alloca i8
ret void
}
define void @signal_handler_with_increment() #1 {
; CHECK-LABEL: signal_handler_with_increment:
; CHECK: push r0
; CHECK-NEXT: in r0, 63
; CHECK-NEXT: push r0
; CHECK-NEXT: push r24
; CHECK-NEXT: lds r24, count
; CHECK-NEXT: inc r24
; CHECK-NEXT: sts count, r24
; CHECK-NEXT: pop r24
; CHECK-NEXT: pop r0
; CHECK-NEXT: out 63, r0
; CHECK-NEXT: pop r0
; CHECK-NEXT: reti
%old = load volatile i8, i8* @count
%new = add i8 %old, 1
store volatile i8 %new, i8* @count
ret void
}
; Check that r1 is saved/restored and set to 0 when using inline assembly.
define void @signal_handler_with_asm() #1 {
; CHECK-LABEL: signal_handler_with_asm:
; CHECK: push r0
; CHECK-NEXT: in r0, 63
; CHECK-NEXT: push r0
; CHECK-NEXT: push r1
; CHECK-NEXT: clr r1
; CHECK-NEXT: push r24
; CHECK-NEXT: ldi
; ;APP
; CHECK: mov
; ;NO_APP
; CHECK: pop r24
; CHECK-NEXT: pop r1
; CHECK-NEXT: pop r0
; CHECK-NEXT: out 63, r0
; CHECK-NEXT: pop r0
; CHECK-NEXT: reti
call i8 asm sideeffect "mov $0, $1", "=r,r"(i8 3) nounwind
ret void
}
declare void @foo()
; When a signal handler calls a function, it must push/pop all call clobbered
; registers.
define void @signal_handler_with_call() #1 {
; CHECK-LABEL: signal_handler_with_call:
; CHECK: push r0
; CHECK-NEXT: in r0, 63
; CHECK-NEXT: push r0
; CHECK-NEXT: push r1
; CHECK-NEXT: clr r1
; CHECK-NEXT: push r18
; CHECK-NEXT: push r19
; CHECK-NEXT: push r20
; CHECK-NEXT: push r21
; CHECK-NEXT: push r22
; CHECK-NEXT: push r23
; CHECK-NEXT: push r24
; CHECK-NEXT: push r25
; CHECK-NEXT: push r26
; CHECK-NEXT: push r27
; CHECK-NEXT: push r30
; CHECK-NEXT: push r31
; CHECK-NEXT: call foo
; CHECK-NEXT: pop r31
; CHECK-NEXT: pop r30
; CHECK-NEXT: pop r27
; CHECK-NEXT: pop r26
; CHECK-NEXT: pop r25
; CHECK-NEXT: pop r24
; CHECK-NEXT: pop r23
; CHECK-NEXT: pop r22
; CHECK-NEXT: pop r21
; CHECK-NEXT: pop r20
; CHECK-NEXT: pop r19
; CHECK-NEXT: pop r18
; CHECK-NEXT: pop r1
; CHECK-NEXT: pop r0
; CHECK-NEXT: out 63, r0
; CHECK-NEXT: pop r0
; CHECK-NEXT: reti
call void @foo()
ret void
}
define void @signal_handler_with_icall() #1 {
; CHECK-LABEL: signal_handler_with_icall:
; CHECK: push r0
; CHECK-NEXT: in r0, 63
; CHECK-NEXT: push r0
; CHECK-NEXT: push r1
; CHECK-NEXT: clr r1
; CHECK-NEXT: push r18
; CHECK-NEXT: push r19
; CHECK-NEXT: push r20
; CHECK-NEXT: push r21
; CHECK-NEXT: push r22
; CHECK-NEXT: push r23
; CHECK-NEXT: push r24
; CHECK-NEXT: push r25
; CHECK-NEXT: push r26
; CHECK-NEXT: push r27
; CHECK-NEXT: push r30
; CHECK-NEXT: push r31
; CHECK-NEXT: lds r30, funcptr
; CHECK-NEXT: lds r31, funcptr+1
; CHECK-NEXT: icall
; CHECK-NEXT: pop r31
; CHECK-NEXT: pop r30
; CHECK-NEXT: pop r27
; CHECK-NEXT: pop r26
; CHECK-NEXT: pop r25
; CHECK-NEXT: pop r24
; CHECK-NEXT: pop r23
; CHECK-NEXT: pop r22
; CHECK-NEXT: pop r21
; CHECK-NEXT: pop r20
; CHECK-NEXT: pop r19
; CHECK-NEXT: pop r18
; CHECK-NEXT: pop r1
; CHECK-NEXT: pop r0
; CHECK-NEXT: out 63, r0
; CHECK-NEXT: pop r0
; CHECK-NEXT: reti
%ptr = load volatile void() addrspace(1)*, void() addrspace(1)** @funcptr
call void %ptr()
ret void
}
attributes #0 = { "interrupt" }
attributes #1 = { "signal" }