|  | ; RUN: llc -mtriple=riscv32 < %s | FileCheck %s -check-prefix=RV32I | 
|  | ; RUN: llc -mtriple=riscv64 < %s | FileCheck %s -check-prefix=RV64I | 
|  | ; RUN: llc -mtriple=riscv32 -mattr=+save-restore < %s | FileCheck %s -check-prefix=RV32I-SR | 
|  | ; RUN: llc -mtriple=riscv64 -mattr=+save-restore < %s | FileCheck %s -check-prefix=RV64I-SR | 
|  | ; RUN: llc -mtriple=riscv32 -mattr=+f,+save-restore -target-abi=ilp32f < %s | FileCheck %s -check-prefix=RV32I-FP-SR | 
|  | ; RUN: llc -mtriple=riscv64 -mattr=+f,+d,+save-restore -target-abi=lp64d < %s | FileCheck %s -check-prefix=RV64I-FP-SR | 
|  |  | 
|  | ; Check that the correct save/restore libcalls are generated. | 
|  |  | 
|  | @var0 = global [18 x i32] zeroinitializer | 
|  | @var1 = global [24 x i32] zeroinitializer | 
|  | @var2 = global [30 x i32] zeroinitializer | 
|  |  | 
|  | define void @callee_saved0() nounwind { | 
|  | ; RV32I-LABEL: callee_saved0: | 
|  | ; RV32I-NOT:     call t0, __riscv_save | 
|  | ; RV32I-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV64I-LABEL: callee_saved0: | 
|  | ; RV64I-NOT:     call t0, __riscv_save | 
|  | ; RV64I-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV32I-SR-LABEL: callee_saved0: | 
|  | ; RV32I-SR:         call t0, __riscv_save_5 | 
|  | ; RV32I-SR:         tail __riscv_restore_5 | 
|  | ; | 
|  | ; RV64I-SR-LABEL: callee_saved0: | 
|  | ; RV64I-SR:         call t0, __riscv_save_5 | 
|  | ; RV64I-SR:         tail __riscv_restore_5 | 
|  | ; | 
|  | ; RV32I-FP-SR-LABEL: callee_saved0: | 
|  | ; RV32I-FP-SR:         call t0, __riscv_save_5 | 
|  | ; RV32I-FP-SR:         tail __riscv_restore_5 | 
|  | ; | 
|  | ; RV64I-FP-SR-LABEL: callee_saved0: | 
|  | ; RV64I-FP-SR:         call t0, __riscv_save_5 | 
|  | ; RV64I-FP-SR:         tail __riscv_restore_5 | 
|  | %val = load [18 x i32], ptr @var0 | 
|  | store volatile [18 x i32] %val, ptr @var0 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @callee_saved1() nounwind { | 
|  | ; RV32I-LABEL: callee_saved1: | 
|  | ; RV32I-NOT:     call t0, __riscv_save | 
|  | ; RV32I-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV64I-LABEL: callee_saved1: | 
|  | ; RV64I-NOT:     call t0, __riscv_save | 
|  | ; RV64I-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV32I-SR-LABEL: callee_saved1: | 
|  | ; RV32I-SR:         call t0, __riscv_save_11 | 
|  | ; RV32I-SR:         tail __riscv_restore_11 | 
|  | ; | 
|  | ; RV64I-SR-LABEL: callee_saved1: | 
|  | ; RV64I-SR:         call t0, __riscv_save_11 | 
|  | ; RV64I-SR:         tail __riscv_restore_11 | 
|  | ; | 
|  | ; RV32I-FP-SR-LABEL: callee_saved1: | 
|  | ; RV32I-FP-SR:         call t0, __riscv_save_11 | 
|  | ; RV32I-FP-SR:         tail __riscv_restore_11 | 
|  | ; | 
|  | ; RV64I-FP-SR-LABEL: callee_saved1: | 
|  | ; RV64I-FP-SR:         call t0, __riscv_save_11 | 
|  | ; RV64I-FP-SR:         tail __riscv_restore_11 | 
|  | %val = load [24 x i32], ptr @var1 | 
|  | store volatile [24 x i32] %val, ptr @var1 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | define void @callee_saved2() nounwind { | 
|  | ; RV32I-LABEL: callee_saved2: | 
|  | ; RV32I-NOT:     call t0, __riscv_save | 
|  | ; RV32I-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV64I-LABEL: callee_saved2: | 
|  | ; RV64I-NOT:     call t0, __riscv_save | 
|  | ; RV64I-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV32I-SR-LABEL: callee_saved2: | 
|  | ; RV32I-SR:         call t0, __riscv_save_12 | 
|  | ; RV32I-SR:         tail __riscv_restore_12 | 
|  | ; | 
|  | ; RV64I-SR-LABEL: callee_saved2: | 
|  | ; RV64I-SR:         call t0, __riscv_save_12 | 
|  | ; RV64I-SR:         tail __riscv_restore_12 | 
|  | ; | 
|  | ; RV32I-FP-SR-LABEL: callee_saved2: | 
|  | ; RV32I-FP-SR:         call t0, __riscv_save_12 | 
|  | ; RV32I-FP-SR:         tail __riscv_restore_12 | 
|  | ; | 
|  | ; RV64I-FP-SR-LABEL: callee_saved2: | 
|  | ; RV64I-FP-SR:         call t0, __riscv_save_12 | 
|  | ; RV64I-FP-SR:         tail __riscv_restore_12 | 
|  | %val = load [30 x i32], ptr @var2 | 
|  | store volatile [30 x i32] %val, ptr @var2 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; Check that floating point callee saved registers are still manually saved and | 
|  | ; restored. | 
|  |  | 
|  | define void @callee_saved_fp() nounwind { | 
|  | ; RV32I-LABEL: callee_saved_fp: | 
|  | ; RV32I-NOT:     call t0, __riscv_save | 
|  | ; RV32I-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV64I-LABEL: callee_saved_fp: | 
|  | ; RV64I-NOT:     call t0, __riscv_save | 
|  | ; RV64I-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV32I-SR-LABEL: callee_saved_fp: | 
|  | ; RV32I-SR:         call t0, __riscv_save_7 | 
|  | ; RV32I-SR:         tail __riscv_restore_7 | 
|  | ; | 
|  | ; RV64I-SR-LABEL: callee_saved_fp: | 
|  | ; RV64I-SR:         call t0, __riscv_save_7 | 
|  | ; RV64I-SR:         tail __riscv_restore_7 | 
|  | ; | 
|  | ; RV32I-FP-SR-LABEL: callee_saved_fp: | 
|  | ; RV32I-FP-SR:         call t0, __riscv_save_7 | 
|  | ; RV32I-FP-SR-NEXT:    addi sp, sp, -16 | 
|  | ; RV32I-FP-SR-NEXT:    fsw fs0, 12(sp) | 
|  | ; RV32I-FP-SR:         flw fs0, 12(sp) | 
|  | ; RV32I-FP-SR-NEXT:    addi sp, sp, 16 | 
|  | ; RV32I-FP-SR-NEXT:    tail __riscv_restore_7 | 
|  | ; | 
|  | ; RV64I-FP-SR-LABEL: callee_saved_fp: | 
|  | ; RV64I-FP-SR:         call t0, __riscv_save_7 | 
|  | ; RV64I-FP-SR-NEXT:    addi sp, sp, -16 | 
|  | ; RV64I-FP-SR-NEXT:    fsd fs0, 8(sp) | 
|  | ; RV64I-FP-SR:         fld fs0, 8(sp) | 
|  | ; RV64I-FP-SR-NEXT:    addi sp, sp, 16 | 
|  | ; RV64I-FP-SR-NEXT:    tail __riscv_restore_7 | 
|  | call void asm sideeffect "", "~{f8},~{x9},~{x18},~{x19},~{x20},~{x21},~{x22}"() | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; Check that preserving tail calls is preferred over save/restore | 
|  |  | 
|  | declare i32 @tail_callee(i32 %i) | 
|  |  | 
|  | define i32 @tail_call(i32 %i) nounwind { | 
|  | ; RV32I-LABEL: tail_call: | 
|  | ; RV32I-NOT:     call t0, __riscv_save | 
|  | ; RV32I:         tail tail_callee | 
|  | ; RV32I-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV64I-LABEL: tail_call: | 
|  | ; RV64I-NOT:     call t0, __riscv_save | 
|  | ; RV64I:         tail tail_callee | 
|  | ; RV64I-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV32I-SR-LABEL: tail_call: | 
|  | ; RV32I-SR-NOT:     call t0, __riscv_save | 
|  | ; RV32I-SR:         tail tail_callee | 
|  | ; RV32I-SR-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV64I-SR-LABEL: tail_call: | 
|  | ; RV64I-SR-NOT:     call t0, __riscv_save | 
|  | ; RV64I-SR:         tail tail_callee | 
|  | ; RV64I-SR-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV32I-FP-SR-LABEL: tail_call: | 
|  | ; RV32I-FP-SR-NOT:     call t0, __riscv_save | 
|  | ; RV32I-FP-SR:         tail tail_callee | 
|  | ; RV32I-FP-SR-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV64I-FP-SR-LABEL: tail_call: | 
|  | ; RV64I-FP-SR-NOT:     call t0, __riscv_save | 
|  | ; RV64I-FP-SR:         tail tail_callee | 
|  | ; RV64I-FP-SR-NOT:     tail __riscv_restore | 
|  | entry: | 
|  | %val = load [18 x i32], ptr @var0 | 
|  | store volatile [18 x i32] %val, ptr @var0 | 
|  | %r = tail call i32 @tail_callee(i32 %i) | 
|  | ret i32 %r | 
|  | } | 
|  |  | 
|  | ; Check that functions with varargs do not use save/restore code | 
|  |  | 
|  | declare void @llvm.va_start(ptr) | 
|  | declare void @llvm.va_end(ptr) | 
|  |  | 
|  | define i32 @varargs(ptr %fmt, ...) nounwind { | 
|  | ; RV32I-LABEL: varargs: | 
|  | ; RV32I-NOT:     call t0, __riscv_save | 
|  | ; RV32I-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV64I-LABEL: varargs: | 
|  | ; RV64I-NOT:     call t0, __riscv_save | 
|  | ; RV64I-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV32I-SR-LABEL: varargs: | 
|  | ; RV32I-SR-NOT:     call t0, __riscv_save | 
|  | ; RV32I-SR-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV64I-SR-LABEL: varargs: | 
|  | ; RV64I-SR-NOT:     call t0, __riscv_save | 
|  | ; RV64I-SR-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV32I-FP-SR-LABEL: varargs: | 
|  | ; RV32I-FP-SR-NOT:     call t0, __riscv_save | 
|  | ; RV32I-FP-SR-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV64I-FP-SR-LABEL: varargs: | 
|  | ; RV64I-FP-SR-NOT:     call t0, __riscv_save | 
|  | ; RV64I-FP-SR-NOT:     tail __riscv_restore | 
|  | %va = alloca ptr, align 4 | 
|  | call void @llvm.va_start(ptr %va) | 
|  | %argp.cur = load ptr, ptr %va, align 4 | 
|  | %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 | 
|  | store ptr %argp.next, ptr %va, align 4 | 
|  | %1 = load i32, ptr %argp.cur, align 4 | 
|  | call void @llvm.va_end(ptr %va) | 
|  | ret i32 %1 | 
|  | } | 
|  |  | 
|  | define void @many_args(i32, i32, i32, i32, i32, i32, i32, i32, i32) nounwind { | 
|  | ; RV32I-LABEL: many_args: | 
|  | ; RV32I-NOT:     call t0, __riscv_save | 
|  | ; RV32I-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV64I-LABEL: many_args: | 
|  | ; RV64I-NOT:     call t0, __riscv_save | 
|  | ; RV64I-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV32I-SR-LABEL: many_args: | 
|  | ; RV32I-SR:         call t0, __riscv_save_5 | 
|  | ; RV32I-SR:         tail __riscv_restore_5 | 
|  | ; | 
|  | ; RV64I-SR-LABEL: many_args: | 
|  | ; RV64I-SR:         call t0, __riscv_save_5 | 
|  | ; RV64I-SR:         tail __riscv_restore_5 | 
|  | ; | 
|  | ; RV32I-FP-SR-LABEL: many_args: | 
|  | ; RV32I-FP-SR:         call t0, __riscv_save_5 | 
|  | ; RV32I-FP-SR:         tail __riscv_restore_5 | 
|  | ; | 
|  | ; RV64I-FP-SR-LABEL: many_args: | 
|  | ; RV64I-FP-SR:         call t0, __riscv_save_5 | 
|  | ; RV64I-FP-SR:         tail __riscv_restore_5 | 
|  | entry: | 
|  | %val = load [18 x i32], ptr @var0 | 
|  | store volatile [18 x i32] %val, ptr @var0 | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; Check that dynamic allocation calculations remain correct | 
|  |  | 
|  | declare ptr @llvm.stacksave() | 
|  | declare void @llvm.stackrestore(ptr) | 
|  | declare void @notdead(ptr) | 
|  |  | 
|  | define void @alloca(i32 %n) nounwind { | 
|  | ; RV32I-LABEL: alloca: | 
|  | ; RV32I-NOT:     call t0, __riscv_save | 
|  | ; RV32I:         addi s0, sp, 16 | 
|  | ; RV32I:         addi sp, s0, -16 | 
|  | ; RV32I-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV64I-LABEL: alloca: | 
|  | ; RV64I-NOT:     call t0, __riscv_save | 
|  | ; RV64I:         addi s0, sp, 32 | 
|  | ; RV64I:         addi sp, s0, -32 | 
|  | ; RV64I-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV32I-SR-LABEL: alloca: | 
|  | ; RV32I-SR:         call t0, __riscv_save_2 | 
|  | ; RV32I-SR:         addi s0, sp, 16 | 
|  | ; RV32I-SR:         addi sp, s0, -16 | 
|  | ; RV32I-SR:         tail __riscv_restore_2 | 
|  | ; | 
|  | ; RV64I-SR-LABEL: alloca: | 
|  | ; RV64I-SR:         call t0, __riscv_save_2 | 
|  | ; RV64I-SR:         addi s0, sp, 32 | 
|  | ; RV64I-SR:         addi sp, s0, -32 | 
|  | ; RV64I-SR:         tail __riscv_restore_2 | 
|  | ; | 
|  | ; RV32I-FP-SR-LABEL: alloca: | 
|  | ; RV32I-FP-SR:         call t0, __riscv_save_2 | 
|  | ; RV32I-FP-SR:         addi s0, sp, 16 | 
|  | ; RV32I-FP-SR:         addi sp, s0, -16 | 
|  | ; RV32I-FP-SR:         tail __riscv_restore_2 | 
|  | ; | 
|  | ; RV64I-FP-SR-LABEL: alloca: | 
|  | ; RV64I-FP-SR:         call t0, __riscv_save_2 | 
|  | ; RV64I-FP-SR:         addi s0, sp, 32 | 
|  | ; RV64I-FP-SR:         addi sp, s0, -32 | 
|  | ; RV64I-FP-SR:         tail __riscv_restore_2 | 
|  | %sp = call ptr @llvm.stacksave() | 
|  | %addr = alloca i8, i32 %n | 
|  | call void @notdead(ptr %addr) | 
|  | call void @llvm.stackrestore(ptr %sp) | 
|  | ret void | 
|  | } | 
|  |  | 
|  | ; Check that functions with interrupt attribute do not use save/restore code | 
|  |  | 
|  | declare i32 @foo(...) | 
|  | define void @interrupt() nounwind "interrupt"="supervisor" { | 
|  | ; RV32I-LABEL: interrupt: | 
|  | ; RV32I-NOT:     call t0, __riscv_save | 
|  | ; RV32I-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV64I-LABEL: interrupt: | 
|  | ; RV64I-NOT:     call t0, __riscv_save | 
|  | ; RV64I-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV32I-SR-LABEL: interrupt: | 
|  | ; RV32I-SR-NOT:     call t0, __riscv_save | 
|  | ; RV32I-SR-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV64I-SR-LABEL: interrupt: | 
|  | ; RV64I-SR-NOT:     call t0, __riscv_save | 
|  | ; RV64I-SR-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV32I-FP-SR-LABEL: interrupt: | 
|  | ; RV32I-FP-SR-NOT:     call t0, __riscv_save | 
|  | ; RV32I-FP-SR-NOT:     tail __riscv_restore | 
|  | ; | 
|  | ; RV64I-FP-SR-LABEL: interrupt: | 
|  | ; RV64I-FP-SR-NOT:     call t0, __riscv_save | 
|  | ; RV64I-FP-SR-NOT:     tail __riscv_restore | 
|  | %call = call i32 @foo() | 
|  | ret void | 
|  | } |