
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
224 lines
5.0 KiB
LLVM
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" }
|