lewis-revill updated this revision to Diff 220677.
lewis-revill added a comment.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Replace internal -mllvm option with target feature enabled through the clang 
frontend using -msave-restore.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D62686/new/

https://reviews.llvm.org/D62686

Files:
  clang/lib/Driver/ToolChains/Arch/RISCV.cpp
  clang/test/Driver/riscv-features.c
  llvm/lib/Target/RISCV/RISCV.td
  llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
  llvm/lib/Target/RISCV/RISCVFrameLowering.h
  llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h
  llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
  llvm/lib/Target/RISCV/RISCVRegisterInfo.h
  llvm/lib/Target/RISCV/RISCVSubtarget.h
  llvm/test/CodeGen/RISCV/saverestore.ll

Index: llvm/test/CodeGen/RISCV/saverestore.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/RISCV/saverestore.ll
@@ -0,0 +1,640 @@
+; 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:         addi sp, sp, -32
+; RV32I-NEXT:    sw s0, 28(sp)
+; RV32I-NEXT:    sw s1, 24(sp)
+; RV32I-NEXT:    sw s2, 20(sp)
+; RV32I-NEXT:    sw s3, 16(sp)
+; RV32I-NEXT:    sw s4, 12(sp)
+; RV32I:         lw s4, 12(sp)
+; RV32I-NEXT:    lw s3, 16(sp)
+; RV32I-NEXT:    lw s2, 20(sp)
+; RV32I-NEXT:    lw s1, 24(sp)
+; RV32I-NEXT:    lw s0, 28(sp)
+; RV32I-NEXT:    addi sp, sp, 32
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: callee_saved0:
+; RV64I:         addi sp, sp, -48
+; RV64I-NEXT:    sd s0, 40(sp)
+; RV64I-NEXT:    sd s1, 32(sp)
+; RV64I-NEXT:    sd s2, 24(sp)
+; RV64I-NEXT:    sd s3, 16(sp)
+; RV64I:         ld s4, 8(sp)
+; RV64I-NEXT:    ld s3, 16(sp)
+; RV64I-NEXT:    ld s2, 24(sp)
+; RV64I-NEXT:    ld s1, 32(sp)
+; RV64I-NEXT:    ld s0, 40(sp)
+; RV64I-NEXT:    addi sp, sp, 48
+; RV64I-NEXT:    ret
+;
+; 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], [18 x i32]* @var0
+  store volatile [18 x i32] %val, [18 x i32]* @var0
+  ret void
+}
+
+define void @callee_saved1() nounwind {
+; RV32I-LABEL: callee_saved1:
+; RV32I:         addi sp, sp, -48
+; RV32I-NEXT:    sw s0, 44(sp)
+; RV32I-NEXT:    sw s1, 40(sp)
+; RV32I-NEXT:    sw s2, 36(sp)
+; RV32I-NEXT:    sw s3, 32(sp)
+; RV32I-NEXT:    sw s4, 28(sp)
+; RV32I-NEXT:    sw s5, 24(sp)
+; RV32I-NEXT:    sw s6, 20(sp)
+; RV32I-NEXT:    sw s7, 16(sp)
+; RV32I-NEXT:    sw s8, 12(sp)
+; RV32I-NEXT:    sw s9, 8(sp)
+; RV32I-NEXT:    sw s10, 4(sp)
+; RV32I:         lw s10, 4(sp)
+; RV32I-NEXT:    lw s9, 8(sp)
+; RV32I-NEXT:    lw s8, 12(sp)
+; RV32I-NEXT:    lw s7, 16(sp)
+; RV32I-NEXT:    lw s6, 20(sp)
+; RV32I-NEXT:    lw s5, 24(sp)
+; RV32I-NEXT:    lw s4, 28(sp)
+; RV32I-NEXT:    lw s3, 32(sp)
+; RV32I-NEXT:    lw s2, 36(sp)
+; RV32I-NEXT:    lw s1, 40(sp)
+; RV32I-NEXT:    lw s0, 44(sp)
+; RV32I-NEXT:    addi sp, sp, 48
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: callee_saved1:
+; RV64I:         addi sp, sp, -96
+; RV64I-NEXT:    sd s0, 88(sp)
+; RV64I-NEXT:    sd s1, 80(sp)
+; RV64I-NEXT:    sd s2, 72(sp)
+; RV64I-NEXT:    sd s3, 64(sp)
+; RV64I-NEXT:    sd s4, 56(sp)
+; RV64I-NEXT:    sd s5, 48(sp)
+; RV64I-NEXT:    sd s6, 40(sp)
+; RV64I-NEXT:    sd s7, 32(sp)
+; RV64I-NEXT:    sd s8, 24(sp)
+; RV64I-NEXT:    sd s9, 16(sp)
+; RV64I-NEXT:    sd s10, 8(sp)
+; RV64I:         ld s10, 8(sp)
+; RV64I-NEXT:    ld s9, 16(sp)
+; RV64I-NEXT:    ld s8, 24(sp)
+; RV64I-NEXT:    ld s7, 32(sp)
+; RV64I-NEXT:    ld s6, 40(sp)
+; RV64I-NEXT:    ld s5, 48(sp)
+; RV64I-NEXT:    ld s4, 56(sp)
+; RV64I-NEXT:    ld s3, 64(sp)
+; RV64I-NEXT:    ld s2, 72(sp)
+; RV64I-NEXT:    ld s1, 80(sp)
+; RV64I-NEXT:    ld s0, 88(sp)
+; RV64I-NEXT:    addi sp, sp, 96
+; RV64I-NEXT:    ret
+;
+; 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], [24 x i32]* @var1
+  store volatile [24 x i32] %val, [24 x i32]* @var1
+  ret void
+}
+
+define void @callee_saved2() nounwind {
+; RV32I-LABEL: callee_saved2:
+; RV32I:         addi sp, sp, -64
+; RV32I-NEXT:    sw s0, 60(sp)
+; RV32I-NEXT:    sw s1, 56(sp)
+; RV32I-NEXT:    sw s2, 52(sp)
+; RV32I-NEXT:    sw s3, 48(sp)
+; RV32I-NEXT:    sw s4, 44(sp)
+; RV32I-NEXT:    sw s5, 40(sp)
+; RV32I-NEXT:    sw s6, 36(sp)
+; RV32I-NEXT:    sw s7, 32(sp)
+; RV32I-NEXT:    sw s8, 28(sp)
+; RV32I-NEXT:    sw s9, 24(sp)
+; RV32I-NEXT:    sw s10, 20(sp)
+; RV32I-NEXT:    sw s11, 16(sp)
+; RV32I:         lw s11, 16(sp)
+; RV32I-NEXT:    lw s10, 20(sp)
+; RV32I-NEXT:    lw s9, 24(sp)
+; RV32I-NEXT:    lw s8, 28(sp)
+; RV32I-NEXT:    lw s7, 32(sp)
+; RV32I-NEXT:    lw s6, 36(sp)
+; RV32I-NEXT:    lw s5, 40(sp)
+; RV32I-NEXT:    lw s4, 44(sp)
+; RV32I-NEXT:    lw s3, 48(sp)
+; RV32I-NEXT:    lw s2, 52(sp)
+; RV32I-NEXT:    lw s1, 56(sp)
+; RV32I-NEXT:    lw s0, 60(sp)
+; RV32I-NEXT:    addi sp, sp, 64
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: callee_saved2:
+; RV64I:         addi sp, sp, -128
+; RV64I-NEXT:    sd s0, 120(sp)
+; RV64I-NEXT:    sd s1, 112(sp)
+; RV64I-NEXT:    sd s2, 104(sp)
+; RV64I-NEXT:    sd s3, 96(sp)
+; RV64I-NEXT:    sd s4, 88(sp)
+; RV64I-NEXT:    sd s5, 80(sp)
+; RV64I-NEXT:    sd s6, 72(sp)
+; RV64I-NEXT:    sd s7, 64(sp)
+; RV64I-NEXT:    sd s8, 56(sp)
+; RV64I-NEXT:    sd s9, 48(sp)
+; RV64I-NEXT:    sd s10, 40(sp)
+; RV64I-NEXT:    sd s11, 32(sp)
+; RV64I:         ld s11, 32(sp)
+; RV64I-NEXT:    ld s10, 40(sp)
+; RV64I-NEXT:    ld s9, 48(sp)
+; RV64I-NEXT:    ld s8, 56(sp)
+; RV64I-NEXT:    ld s7, 64(sp)
+; RV64I-NEXT:    ld s6, 72(sp)
+; RV64I-NEXT:    ld s5, 80(sp)
+; RV64I-NEXT:    ld s4, 88(sp)
+; RV64I-NEXT:    ld s3, 96(sp)
+; RV64I-NEXT:    ld s2, 104(sp)
+; RV64I-NEXT:    ld s1, 112(sp)
+; RV64I-NEXT:    ld s0, 120(sp)
+; RV64I-NEXT:    addi sp, sp, 128
+; RV64I-NEXT:    ret
+;
+; 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], [30 x i32]* @var2
+  store volatile [30 x i32] %val, [30 x i32]* @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:         addi sp, sp, -32
+; RV32I-NEXT:    sw s1, 28(sp)
+; RV32I-NEXT:    sw s2, 24(sp)
+; RV32I-NEXT:    sw s3, 20(sp)
+; RV32I-NEXT:    sw s4, 16(sp)
+; RV32I-NEXT:    sw s5, 12(sp)
+; RV32I-NEXT:    sw s6, 8(sp)
+; RV32I:         lw s6, 8(sp)
+; RV32I-NEXT:    lw s5, 12(sp)
+; RV32I-NEXT:    lw s4, 16(sp)
+; RV32I-NEXT:    lw s3, 20(sp)
+; RV32I-NEXT:    lw s2, 24(sp)
+; RV32I-NEXT:    lw s1, 28(sp)
+; RV32I-NEXT:    addi sp, sp, 32
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: callee_saved_fp:
+; RV64I:         addi sp, sp, -48
+; RV64I-NEXT:    sd s1, 40(sp)
+; RV64I-NEXT:    sd s2, 32(sp)
+; RV64I-NEXT:    sd s3, 24(sp)
+; RV64I-NEXT:    sd s4, 16(sp)
+; RV64I-NEXT:    sd s5, 8(sp)
+; RV64I-NEXT:    sd s6, 0(sp)
+; RV64I:         ld s6, 0(sp)
+; RV64I-NEXT:    ld s5, 8(sp)
+; RV64I-NEXT:    ld s4, 16(sp)
+; RV64I-NEXT:    ld s3, 24(sp)
+; RV64I-NEXT:    ld s2, 32(sp)
+; RV64I-NEXT:    ld s1, 40(sp)
+; RV64I-NEXT:    addi sp, sp, 48
+; RV64I-NEXT:    ret
+;
+; 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 tail calls are updated correctly by save/restore
+
+declare i32 @tail_callee(i32 %i)
+
+define i32 @tail_call(i32 %i) nounwind {
+; RV32I-LABEL: tail_call:
+; RV32I:         addi sp, sp, -32
+; RV32I-NEXT:    sw s0, 28(sp)
+; RV32I-NEXT:    sw s1, 24(sp)
+; RV32I-NEXT:    sw s2, 20(sp)
+; RV32I-NEXT:    sw s3, 16(sp)
+; RV32I-NEXT:    sw s4, 12(sp)
+; RV32I-NEXT:    sw s5, 8(sp)
+; RV32I:         lw s5, 8(sp)
+; RV32I-NEXT:    lw s4, 12(sp)
+; RV32I-NEXT:    lw s3, 16(sp)
+; RV32I-NEXT:    lw s2, 20(sp)
+; RV32I-NEXT:    lw s1, 24(sp)
+; RV32I-NEXT:    lw s0, 28(sp)
+; RV32I-NEXT:    addi sp, sp, 32
+; RV32I-NEXT:    tail tail_callee
+;
+; RV64I-LABEL: tail_call:
+; RV64I:         addi sp, sp, -48
+; RV64I-NEXT:    sd s0, 40(sp)
+; RV64I-NEXT:    sd s1, 32(sp)
+; RV64I-NEXT:    sd s2, 24(sp)
+; RV64I-NEXT:    sd s3, 16(sp)
+; RV64I-NEXT:    sd s4, 8(sp)
+; RV64I-NEXT:    sd s5, 0(sp)
+; RV64I:         ld s5, 0(sp)
+; RV64I-NEXT:    ld s4, 8(sp)
+; RV64I-NEXT:    ld s3, 16(sp)
+; RV64I-NEXT:    ld s2, 24(sp)
+; RV64I-NEXT:    ld s1, 32(sp)
+; RV64I-NEXT:    ld s0, 40(sp)
+; RV64I-NEXT:    addi sp, sp, 48
+; RV64I-NEXT:    tail tail_callee
+;
+; RV32I-SR-LABEL: tail_call:
+; RV32I-SR:         call t0, __riscv_save_6
+; RV32I-SR:         call tail_callee
+; RV32I-SR-NEXT:    tail __riscv_restore_6
+;
+; RV64I-SR-LABEL: tail_call:
+; RV64I-SR:         call t0, __riscv_save_6
+; RV64I-SR:         call tail_callee
+; RV64I-SR-NEXT:    tail __riscv_restore_6
+;
+; RV32I-FP-SR-LABEL: tail_call:
+; RV32I-FP-SR:         call t0, __riscv_save_6
+; RV32I-FP-SR:         call tail_callee
+; RV32I-FP-SR-NEXT:    tail __riscv_restore_6
+;
+; RV64I-FP-SR-LABEL: tail_call:
+; RV64I-FP-SR:         call t0, __riscv_save_6
+; RV64I-FP-SR:         call tail_callee
+; RV64I-FP-SR-NEXT:    tail __riscv_restore_6
+entry:
+  %val = load [18 x i32], [18 x i32]* @var0
+  store volatile [18 x i32] %val, [18 x i32]* @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(i8*)
+declare void @llvm.va_end(i8*)
+
+define i32 @varargs(i8* %fmt, ...) nounwind {
+; RV32I-LABEL: varargs:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -48
+; RV32I-NEXT:    mv a0, a1
+; RV32I-NEXT:    sw a7, 44(sp)
+; RV32I-NEXT:    sw a6, 40(sp)
+; RV32I-NEXT:    sw a5, 36(sp)
+; RV32I-NEXT:    sw a4, 32(sp)
+; RV32I-NEXT:    sw a3, 28(sp)
+; RV32I-NEXT:    sw a2, 24(sp)
+; RV32I-NEXT:    addi a1, sp, 24
+; RV32I-NEXT:    sw a1, 12(sp)
+; RV32I-NEXT:    sw a0, 20(sp)
+; RV32I-NEXT:    addi sp, sp, 48
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: varargs:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -80
+; RV64I-NEXT:    sd a1, 24(sp)
+; RV64I-NEXT:    sd a7, 72(sp)
+; RV64I-NEXT:    sd a6, 64(sp)
+; RV64I-NEXT:    sd a5, 56(sp)
+; RV64I-NEXT:    sd a4, 48(sp)
+; RV64I-NEXT:    sd a3, 40(sp)
+; RV64I-NEXT:    sd a2, 32(sp)
+; RV64I-NEXT:    addi a0, sp, 24
+; RV64I-NEXT:    ori a0, a0, 4
+; RV64I-NEXT:    sd a0, 8(sp)
+; RV64I-NEXT:    lw a0, 24(sp)
+; RV64I-NEXT:    addi sp, sp, 80
+; RV64I-NEXT:    ret
+;
+; RV32I-SR-LABEL: varargs:
+; RV32I-SR:       # %bb.0:
+; RV32I-SR-NEXT:    addi sp, sp, -48
+; RV32I-SR-NEXT:    mv a0, a1
+; RV32I-SR-NEXT:    sw a7, 44(sp)
+; RV32I-SR-NEXT:    sw a6, 40(sp)
+; RV32I-SR-NEXT:    sw a5, 36(sp)
+; RV32I-SR-NEXT:    sw a4, 32(sp)
+; RV32I-SR-NEXT:    sw a3, 28(sp)
+; RV32I-SR-NEXT:    sw a2, 24(sp)
+; RV32I-SR-NEXT:    addi a1, sp, 24
+; RV32I-SR-NEXT:    sw a1, 12(sp)
+; RV32I-SR-NEXT:    sw a0, 20(sp)
+; RV32I-SR-NEXT:    addi sp, sp, 48
+; RV32I-SR-NEXT:    ret
+;
+; RV64I-SR-LABEL: varargs:
+; RV64I-SR:       # %bb.0:
+; RV64I-SR-NEXT:    addi sp, sp, -80
+; RV64I-SR-NEXT:    sd a1, 24(sp)
+; RV64I-SR-NEXT:    sd a7, 72(sp)
+; RV64I-SR-NEXT:    sd a6, 64(sp)
+; RV64I-SR-NEXT:    sd a5, 56(sp)
+; RV64I-SR-NEXT:    sd a4, 48(sp)
+; RV64I-SR-NEXT:    sd a3, 40(sp)
+; RV64I-SR-NEXT:    sd a2, 32(sp)
+; RV64I-SR-NEXT:    addi a0, sp, 24
+; RV64I-SR-NEXT:    ori a0, a0, 4
+; RV64I-SR-NEXT:    sd a0, 8(sp)
+; RV64I-SR-NEXT:    lw a0, 24(sp)
+; RV64I-SR-NEXT:    addi sp, sp, 80
+; RV64I-SR-NEXT:    ret
+;
+; RV32I-FP-SR-LABEL: varargs:
+; RV32I-FP-SR:       # %bb.0:
+; RV32I-FP-SR-NEXT:    addi sp, sp, -48
+; RV32I-FP-SR-NEXT:    mv a0, a1
+; RV32I-FP-SR-NEXT:    sw a7, 44(sp)
+; RV32I-FP-SR-NEXT:    sw a6, 40(sp)
+; RV32I-FP-SR-NEXT:    sw a5, 36(sp)
+; RV32I-FP-SR-NEXT:    sw a4, 32(sp)
+; RV32I-FP-SR-NEXT:    sw a3, 28(sp)
+; RV32I-FP-SR-NEXT:    sw a2, 24(sp)
+; RV32I-FP-SR-NEXT:    addi a1, sp, 24
+; RV32I-FP-SR-NEXT:    sw a1, 12(sp)
+; RV32I-FP-SR-NEXT:    sw a0, 20(sp)
+; RV32I-FP-SR-NEXT:    addi sp, sp, 48
+; RV32I-FP-SR-NEXT:    ret
+;
+; RV64I-FP-SR-LABEL: varargs:
+; RV64I-FP-SR:       # %bb.0:
+; RV64I-FP-SR-NEXT:    addi sp, sp, -80
+; RV64I-FP-SR-NEXT:    sd a1, 24(sp)
+; RV64I-FP-SR-NEXT:    sd a7, 72(sp)
+; RV64I-FP-SR-NEXT:    sd a6, 64(sp)
+; RV64I-FP-SR-NEXT:    sd a5, 56(sp)
+; RV64I-FP-SR-NEXT:    sd a4, 48(sp)
+; RV64I-FP-SR-NEXT:    sd a3, 40(sp)
+; RV64I-FP-SR-NEXT:    sd a2, 32(sp)
+; RV64I-FP-SR-NEXT:    addi a0, sp, 24
+; RV64I-FP-SR-NEXT:    ori a0, a0, 4
+; RV64I-FP-SR-NEXT:    sd a0, 8(sp)
+; RV64I-FP-SR-NEXT:    lw a0, 24(sp)
+; RV64I-FP-SR-NEXT:    addi sp, sp, 80
+; RV64I-FP-SR-NEXT:    ret
+  %va = alloca i8*, align 4
+  %1 = bitcast i8** %va to i8*
+  call void @llvm.va_start(i8* %1)
+  %argp.cur = load i8*, i8** %va, align 4
+  %argp.next = getelementptr inbounds i8, i8* %argp.cur, i32 4
+  store i8* %argp.next, i8** %va, align 4
+  %2 = bitcast i8* %argp.cur to i32*
+  %3 = load i32, i32* %2, align 4
+  call void @llvm.va_end(i8* %1)
+  ret i32 %3
+}
+
+define void @many_args(i32, i32, i32, i32, i32, i32, i32, i32, i32) nounwind {
+; RV32I-LABEL: many_args:
+; RV32I:         addi sp, sp, -32
+; RV32I-NEXT:    sw s0, 28(sp)
+; RV32I-NEXT:    sw s1, 24(sp)
+; RV32I-NEXT:    sw s2, 20(sp)
+; RV32I-NEXT:    sw s3, 16(sp)
+; RV32I-NEXT:    sw s4, 12(sp)
+; RV32I:         lw s4, 12(sp)
+; RV32I-NEXT:    lw s3, 16(sp)
+; RV32I-NEXT:    lw s2, 20(sp)
+; RV32I-NEXT:    lw s1, 24(sp)
+; RV32I-NEXT:    lw s0, 28(sp)
+; RV32I-NEXT:    addi sp, sp, 32
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: many_args:
+; RV64I:         addi sp, sp, -48
+; RV64I-NEXT:    sd s0, 40(sp)
+; RV64I-NEXT:    sd s1, 32(sp)
+; RV64I-NEXT:    sd s2, 24(sp)
+; RV64I-NEXT:    sd s3, 16(sp)
+; RV64I-NEXT:    sd s4, 8(sp)
+; RV64I:         ld s4, 8(sp)
+; RV64I-NEXT:    ld s3, 16(sp)
+; RV64I-NEXT:    ld s2, 24(sp)
+; RV64I-NEXT:    ld s1, 32(sp)
+; RV64I-NEXT:    ld s0, 40(sp)
+; RV64I-NEXT:    addi sp, sp, 48
+; RV64I-NEXT:    ret
+;
+; 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], [18 x i32]* @var0
+  store volatile [18 x i32] %val, [18 x i32]* @var0
+  ret void
+}
+
+; Check that dynamic allocation calculations remain correct
+
+declare i8* @llvm.stacksave()
+declare void @llvm.stackrestore(i8*)
+declare void @notdead(i8*)
+
+define void @alloca(i32 %n) nounwind {
+; RV32I-LABEL: alloca:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    sw ra, 12(sp)
+; RV32I-NEXT:    sw s0, 8(sp)
+; RV32I-NEXT:    sw s1, 4(sp)
+; RV32I-NEXT:    addi s0, sp, 16
+; RV32I-NEXT:    mv s1, sp
+; RV32I-NEXT:    addi a0, a0, 15
+; RV32I-NEXT:    andi a0, a0, -16
+; RV32I-NEXT:    sub a0, sp, a0
+; RV32I-NEXT:    mv sp, a0
+; RV32I-NEXT:    call notdead
+; RV32I-NEXT:    mv sp, s1
+; RV32I-NEXT:    addi sp, s0, -16
+; RV32I-NEXT:    lw s1, 4(sp)
+; RV32I-NEXT:    lw s0, 8(sp)
+; RV32I-NEXT:    lw ra, 12(sp)
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: alloca:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -32
+; RV64I-NEXT:    sd ra, 24(sp)
+; RV64I-NEXT:    sd s0, 16(sp)
+; RV64I-NEXT:    sd s1, 8(sp)
+; RV64I-NEXT:    addi s0, sp, 32
+; RV64I-NEXT:    addi a1, zero, 1
+; RV64I-NEXT:    slli a1, a1, 33
+; RV64I-NEXT:    addi a1, a1, -16
+; RV64I-NEXT:    slli a0, a0, 32
+; RV64I-NEXT:    srli a0, a0, 32
+; RV64I-NEXT:    addi a0, a0, 15
+; RV64I-NEXT:    and a0, a0, a1
+; RV64I-NEXT:    mv s1, sp
+; RV64I-NEXT:    sub a0, sp, a0
+; RV64I-NEXT:    mv sp, a0
+; RV64I-NEXT:    call notdead
+; RV64I-NEXT:    mv sp, s1
+; RV64I-NEXT:    addi sp, s0, -32
+; RV64I-NEXT:    ld s1, 8(sp)
+; RV64I-NEXT:    ld s0, 16(sp)
+; RV64I-NEXT:    ld ra, 24(sp)
+; RV64I-NEXT:    addi sp, sp, 32
+; RV64I-NEXT:    ret
+;
+; RV32I-SR-LABEL: alloca:
+; RV32I-SR:       # %bb.0:
+; RV32I-SR-NEXT:    call t0, __riscv_save_2
+; RV32I-SR-NEXT:    mv s0, sp
+; RV32I-SR-NEXT:    mv s1, sp
+; RV32I-SR-NEXT:    addi a0, a0, 15
+; RV32I-SR-NEXT:    andi a0, a0, -16
+; RV32I-SR-NEXT:    sub a0, sp, a0
+; RV32I-SR-NEXT:    mv sp, a0
+; RV32I-SR-NEXT:    call notdead
+; RV32I-SR-NEXT:    mv sp, s1
+; RV32I-SR-NEXT:    mv sp, s0
+; RV32I-SR-NEXT:    tail __riscv_restore_2
+;
+; RV64I-SR-LABEL: alloca:
+; RV64I-SR:       # %bb.0:
+; RV64I-SR-NEXT:    call t0, __riscv_save_2
+; RV64I-SR-NEXT:    mv s0, sp
+; RV64I-SR-NEXT:    addi a1, zero, 1
+; RV64I-SR-NEXT:    slli a1, a1, 33
+; RV64I-SR-NEXT:    addi a1, a1, -16
+; RV64I-SR-NEXT:    slli a0, a0, 32
+; RV64I-SR-NEXT:    srli a0, a0, 32
+; RV64I-SR-NEXT:    addi a0, a0, 15
+; RV64I-SR-NEXT:    and a0, a0, a1
+; RV64I-SR-NEXT:    mv s1, sp
+; RV64I-SR-NEXT:    sub a0, sp, a0
+; RV64I-SR-NEXT:    mv sp, a0
+; RV64I-SR-NEXT:    call notdead
+; RV64I-SR-NEXT:    mv sp, s1
+; RV64I-SR-NEXT:    mv sp, s0
+; RV64I-SR-NEXT:    tail __riscv_restore_2
+;
+; RV32I-FP-SR-LABEL: alloca:
+; RV32I-FP-SR:       # %bb.0:
+; RV32I-FP-SR-NEXT:    call t0, __riscv_save_2
+; RV32I-FP-SR-NEXT:    mv s0, sp
+; RV32I-FP-SR-NEXT:    mv s1, sp
+; RV32I-FP-SR-NEXT:    addi a0, a0, 15
+; RV32I-FP-SR-NEXT:    andi a0, a0, -16
+; RV32I-FP-SR-NEXT:    sub a0, sp, a0
+; RV32I-FP-SR-NEXT:    mv sp, a0
+; RV32I-FP-SR-NEXT:    call notdead
+; RV32I-FP-SR-NEXT:    mv sp, s1
+; RV32I-FP-SR-NEXT:    mv sp, s0
+; RV32I-FP-SR-NEXT:    tail __riscv_restore_2
+;
+; RV64I-FP-SR-LABEL: alloca:
+; RV64I-FP-SR:       # %bb.0:
+; RV64I-FP-SR-NEXT:    call t0, __riscv_save_2
+; RV64I-FP-SR-NEXT:    mv s0, sp
+; RV64I-FP-SR-NEXT:    addi a1, zero, 1
+; RV64I-FP-SR-NEXT:    slli a1, a1, 33
+; RV64I-FP-SR-NEXT:    addi a1, a1, -16
+; RV64I-FP-SR-NEXT:    slli a0, a0, 32
+; RV64I-FP-SR-NEXT:    srli a0, a0, 32
+; RV64I-FP-SR-NEXT:    addi a0, a0, 15
+; RV64I-FP-SR-NEXT:    and a0, a0, a1
+; RV64I-FP-SR-NEXT:    mv s1, sp
+; RV64I-FP-SR-NEXT:    sub a0, sp, a0
+; RV64I-FP-SR-NEXT:    mv sp, a0
+; RV64I-FP-SR-NEXT:    call notdead
+; RV64I-FP-SR-NEXT:    mv sp, s1
+; RV64I-FP-SR-NEXT:    mv sp, s0
+; RV64I-FP-SR-NEXT:    tail __riscv_restore_2
+  %sp = call i8* @llvm.stacksave()
+  %addr = alloca i8, i32 %n
+  call void @notdead(i8* %addr)
+  call void @llvm.stackrestore(i8* %sp)
+  ret void
+}
Index: llvm/lib/Target/RISCV/RISCVSubtarget.h
===================================================================
--- llvm/lib/Target/RISCV/RISCVSubtarget.h
+++ llvm/lib/Target/RISCV/RISCVSubtarget.h
@@ -43,6 +43,7 @@
   bool IsRV32E = false;
   bool EnableLinkerRelax = false;
   bool EnableRVCHintInstrs = false;
+  bool EnableSaveRestore = false;
   unsigned XLen = 32;
   MVT XLenVT = MVT::i32;
   RISCVABI::ABI TargetABI = RISCVABI::ABI_Unknown;
@@ -90,6 +91,7 @@
   bool isRV32E() const { return IsRV32E; }
   bool enableLinkerRelax() const { return EnableLinkerRelax; }
   bool enableRVCHintInstrs() const { return EnableRVCHintInstrs; }
+  bool enableSaveRestore() const { return EnableSaveRestore; }
   MVT getXLenVT() const { return XLenVT; }
   unsigned getXLen() const { return XLen; }
   RISCVABI::ABI getTargetABI() const { return TargetABI; }
Index: llvm/lib/Target/RISCV/RISCVRegisterInfo.h
===================================================================
--- llvm/lib/Target/RISCV/RISCVRegisterInfo.h
+++ llvm/lib/Target/RISCV/RISCVRegisterInfo.h
@@ -35,6 +35,9 @@
 
   const uint32_t *getNoPreservedMask() const override;
 
+  bool hasReservedSpillSlot(const MachineFunction &MF, unsigned Reg,
+                            int &FrameIdx) const override;
+
   void eliminateFrameIndex(MachineBasicBlock::iterator MI, int SPAdj,
                            unsigned FIOperandNum,
                            RegScavenger *RS = nullptr) const override;
Index: llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
===================================================================
--- llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
+++ llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
@@ -12,6 +12,7 @@
 
 #include "RISCVRegisterInfo.h"
 #include "RISCV.h"
+#include "RISCVMachineFunctionInfo.h"
 #include "RISCVSubtarget.h"
 #include "llvm/CodeGen/MachineFrameInfo.h"
 #include "llvm/CodeGen/MachineFunction.h"
@@ -80,6 +81,38 @@
   return CSR_NoRegs_RegMask;
 }
 
+// Frame indexes representing locations of CSRs which are given a fixed location
+// by save/restore libcalls.
+static std::map<unsigned, int> FixedCSRFIMap = {
+  {/*ra*/  RISCV::X1,   -1},
+  {/*s0*/  RISCV::X8,   -2},
+  {/*s1*/  RISCV::X9,   -3},
+  {/*s2*/  RISCV::X18,  -4},
+  {/*s3*/  RISCV::X19,  -5},
+  {/*s4*/  RISCV::X20,  -6},
+  {/*s5*/  RISCV::X21,  -7},
+  {/*s6*/  RISCV::X22,  -8},
+  {/*s7*/  RISCV::X23,  -9},
+  {/*s8*/  RISCV::X24,  -10},
+  {/*s9*/  RISCV::X25,  -11},
+  {/*s10*/ RISCV::X26,  -12},
+  {/*s11*/ RISCV::X27,  -13}};
+
+bool RISCVRegisterInfo::hasReservedSpillSlot(const MachineFunction &MF,
+                                             unsigned Reg,
+                                             int &FrameIdx) const {
+  const auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
+  if (!RVFI->useSaveRestoreLibCalls())
+    return false;
+
+  auto FII = FixedCSRFIMap.find(Reg);
+  if (FII == FixedCSRFIMap.end())
+    return false;
+
+  FrameIdx = FII->second;
+  return true;
+}
+
 void RISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
                                             int SPAdj, unsigned FIOperandNum,
                                             RegScavenger *RS) const {
Index: llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h
===================================================================
--- llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h
+++ llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.h
@@ -13,6 +13,7 @@
 #ifndef LLVM_LIB_TARGET_RISCV_RISCVMACHINEFUNCTIONINFO_H
 #define LLVM_LIB_TARGET_RISCV_RISCVMACHINEFUNCTIONINFO_H
 
+#include "RISCVSubtarget.h"
 #include "llvm/CodeGen/MachineFrameInfo.h"
 #include "llvm/CodeGen/MachineFunction.h"
 
@@ -45,6 +46,13 @@
       MoveF64FrameIndex = MF.getFrameInfo().CreateStackObject(8, 8, false);
     return MoveF64FrameIndex;
   }
+
+  bool useSaveRestoreLibCalls() const {
+    // We cannot use fixed locations for the callee saved spill slots if the
+    // function uses a varargs save area.
+    return MF.getSubtarget<RISCVSubtarget>().enableSaveRestore() &&
+           VarArgsSaveSize == 0;
+  }
 };
 
 } // end namespace llvm
Index: llvm/lib/Target/RISCV/RISCVFrameLowering.h
===================================================================
--- llvm/lib/Target/RISCV/RISCVFrameLowering.h
+++ llvm/lib/Target/RISCV/RISCVFrameLowering.h
@@ -44,6 +44,15 @@
   MachineBasicBlock::iterator
   eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB,
                                 MachineBasicBlock::iterator MI) const override;
+  bool spillCalleeSavedRegisters(MachineBasicBlock &MBB,
+                                 MachineBasicBlock::iterator MI,
+                                 const std::vector<CalleeSavedInfo> &CSI,
+                                 const TargetRegisterInfo *TRI) const override;
+  bool
+  restoreCalleeSavedRegisters(MachineBasicBlock &MBB,
+                              MachineBasicBlock::iterator MI,
+                              std::vector<CalleeSavedInfo> &CSI,
+                              const TargetRegisterInfo *TRI) const override;
 
 protected:
   const RISCVSubtarget &STI;
Index: llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
===================================================================
--- llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -97,6 +97,17 @@
 // Returns the register used to hold the stack pointer.
 static Register getSPReg(const RISCVSubtarget &STI) { return RISCV::X2; }
 
+static std::vector<CalleeSavedInfo>
+getNonLibcallCSI(const std::vector<CalleeSavedInfo> &CSI) {
+  std::vector<CalleeSavedInfo> NonLibcallCSI;
+
+  for (auto &CS : CSI)
+    if (CS.getFrameIdx() >= 0)
+      NonLibcallCSI.push_back(CS);
+
+  return NonLibcallCSI;
+}
+
 void RISCVFrameLowering::emitPrologue(MachineFunction &MF,
                                       MachineBasicBlock &MBB) const {
   assert(&MF.front() == &MBB && "Shrink-wrapping not yet supported");
@@ -116,6 +127,11 @@
   Register FPReg = getFPReg(STI);
   Register SPReg = getSPReg(STI);
 
+  // Since spillCalleeSavedRegisters may have inserted a libcall, skip past
+  // any instructions marked as FrameSetup
+  while (MBBI != MBB.end() && MBBI->getFlag(MachineInstr::FrameSetup))
+    ++MBBI;
+
   // Debug location must be unknown since the first debug location is used
   // to determine the end of the prologue.
   DebugLoc DL;
@@ -140,24 +156,27 @@
   BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
       .addCFIIndex(CFIIndex);
 
-  // The frame pointer is callee-saved, and code has been generated for us to
-  // save it to the stack. We need to skip over the storing of callee-saved
-  // registers as the frame pointer must be modified after it has been saved
-  // to the stack, not before.
-  // FIXME: assumes exactly one instruction is used to save each callee-saved
-  // register.
-  const std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo();
-  std::advance(MBBI, CSI.size());
-
-  // Iterate over list of callee-saved registers and emit .cfi_offset
-  // directives.
-  for (const auto &Entry : CSI) {
-    int64_t Offset = MFI.getObjectOffset(Entry.getFrameIdx());
-    Register Reg = Entry.getReg();
-    unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset(
-        nullptr, RI->getDwarfRegNum(Reg, true), Offset));
-    BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
-        .addCFIIndex(CFIIndex);
+  const std::vector<CalleeSavedInfo> &CSI =
+      getNonLibcallCSI(MFI.getCalleeSavedInfo());
+  if (!CSI.empty()) {
+    // The frame pointer is callee-saved, and code has been generated for us to
+    // save it to the stack. We need to skip over the storing of callee-saved
+    // registers as the frame pointer must be modified after it has been saved
+    // to the stack, not before.
+    // FIXME: assumes exactly one instruction is used to save each callee-saved
+    // register.
+    std::advance(MBBI, CSI.size());
+
+    // Iterate over list of callee-saved registers and emit .cfi_offset
+    // directives.
+    for (const auto &Entry : CSI) {
+      int64_t Offset = MFI.getObjectOffset(Entry.getFrameIdx());
+      Register Reg = Entry.getReg();
+      unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset(
+          nullptr, RI->getDwarfRegNum(Reg, true), Offset));
+      BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
+          .addCFIIndex(CFIIndex);
+    }
   }
 
   // Generate new FP.
@@ -207,10 +226,21 @@
   Register FPReg = getFPReg(STI);
   Register SPReg = getSPReg(STI);
 
+  // If callee-saved registers are saved via libcall, place stack adjustment
+  // before this call.
+  while (MBBI != MBB.begin() &&
+         std::prev(MBBI)->getFlag(MachineInstr::FrameDestroy))
+    --MBBI;
+
+  const std::vector<CalleeSavedInfo> &CSI =
+      getNonLibcallCSI(MFI.getCalleeSavedInfo());
+
   // Skip to before the restores of callee-saved registers
   // FIXME: assumes exactly one instruction is used to restore each
   // callee-saved register.
-  auto LastFrameDestroy = std::prev(MBBI, MFI.getCalleeSavedInfo().size());
+  auto LastFrameDestroy = MBBI;
+  if (!CSI.empty())
+    LastFrameDestroy = std::prev(MBBI, CSI.size());
 
   uint64_t StackSize = MFI.getStackSize();
   uint64_t FPOffset = StackSize - RVFI->getVarArgsSaveSize();
@@ -245,15 +275,16 @@
   }
 
   // Add CFI directives for callee-saved registers.
-  const std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo();
-  // Iterate over list of callee-saved registers and emit .cfi_restore
-  // directives.
-  for (const auto &Entry : CSI) {
-    Register Reg = Entry.getReg();
-    unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createRestore(
-        nullptr, RI->getDwarfRegNum(Reg, true)));
-    BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
-        .addCFIIndex(CFIIndex);
+  if (!CSI.empty()) {
+    // Iterate over list of callee-saved registers and emit .cfi_restore
+    // directives.
+    for (const auto &Entry : CSI) {
+      Register Reg = Entry.getReg();
+      unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createRestore(
+          nullptr, RI->getDwarfRegNum(Reg, true)));
+      BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))
+          .addCFIIndex(CFIIndex);
+    }
   }
 
   // Deallocate stack
@@ -404,3 +435,167 @@
 
   return MBB.erase(MI);
 }
+
+// Get the name of the libcall used for spilling callee saved registers.
+// If this function will not use save/restore libcalls, then return a nullptr.
+static const char *
+getSpillLibCallName(MachineFunction &MF,
+                    const std::vector<CalleeSavedInfo> &CSI) {
+  auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
+
+  if (CSI.empty() || !RVFI->useSaveRestoreLibCalls())
+    return nullptr;
+
+  unsigned MaxReg = 0;
+  for (auto &CS : CSI)
+    if (CS.getFrameIdx() < 0)
+      MaxReg = std::max(MaxReg, CS.getReg());
+
+  if (MaxReg == 0)
+    return nullptr;
+
+  switch (MaxReg) {
+  default:
+    llvm_unreachable("Something has gone wrong!");
+  case /*s11*/ RISCV::X27: return "__riscv_save_12";
+  case /*s10*/ RISCV::X26: return "__riscv_save_11";
+  case /*s9*/  RISCV::X25: return "__riscv_save_10";
+  case /*s8*/  RISCV::X24: return "__riscv_save_9";
+  case /*s7*/  RISCV::X23: return "__riscv_save_8";
+  case /*s6*/  RISCV::X22: return "__riscv_save_7";
+  case /*s5*/  RISCV::X21: return "__riscv_save_6";
+  case /*s4*/  RISCV::X20: return "__riscv_save_5";
+  case /*s3*/  RISCV::X19: return "__riscv_save_4";
+  case /*s2*/  RISCV::X18: return "__riscv_save_3";
+  case /*s1*/  RISCV::X9:  return "__riscv_save_2";
+  case /*s0*/  RISCV::X8:  return "__riscv_save_1";
+  case /*ra*/  RISCV::X1:  return "__riscv_save_0";
+  }
+}
+
+// Get the name of the libcall used for restoring callee saved registers.
+// If this function will not use save/restore libcalls, then return a nullptr.
+static const char *
+getRestoreLibCallName(MachineFunction &MF,
+                      const std::vector<CalleeSavedInfo> &CSI) {
+  auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
+
+  if (CSI.empty() || !RVFI->useSaveRestoreLibCalls())
+    return nullptr;
+
+  unsigned MaxReg = 0;
+  for (auto &CS : CSI)
+    if (CS.getFrameIdx() < 0)
+      MaxReg = std::max(MaxReg, CS.getReg());
+
+  if (MaxReg == 0)
+    return nullptr;
+
+  switch (MaxReg) {
+  default:
+    llvm_unreachable("Something has gone wrong!");
+  case /*s11*/ RISCV::X27: return "__riscv_restore_12";
+  case /*s10*/ RISCV::X26: return "__riscv_restore_11";
+  case /*s9*/  RISCV::X25: return "__riscv_restore_10";
+  case /*s8*/  RISCV::X24: return "__riscv_restore_9";
+  case /*s7*/  RISCV::X23: return "__riscv_restore_8";
+  case /*s6*/  RISCV::X22: return "__riscv_restore_7";
+  case /*s5*/  RISCV::X21: return "__riscv_restore_6";
+  case /*s4*/  RISCV::X20: return "__riscv_restore_5";
+  case /*s3*/  RISCV::X19: return "__riscv_restore_4";
+  case /*s2*/  RISCV::X18: return "__riscv_restore_3";
+  case /*s1*/  RISCV::X9:  return "__riscv_restore_2";
+  case /*s0*/  RISCV::X8:  return "__riscv_restore_1";
+  case /*ra*/  RISCV::X1:  return "__riscv_restore_0";
+  }
+}
+
+bool RISCVFrameLowering::spillCalleeSavedRegisters(
+    MachineBasicBlock &MBB, MachineBasicBlock::iterator MI,
+    const std::vector<CalleeSavedInfo> &CSI,
+    const TargetRegisterInfo *TRI) const {
+  if (CSI.empty())
+    return true;
+
+  MachineFunction *MF = MBB.getParent();
+  const TargetInstrInfo &TII = *MF->getSubtarget().getInstrInfo();
+  DebugLoc DL;
+  if (MI != MBB.end() && !MI->isDebugInstr())
+    DL = MI->getDebugLoc();
+
+  const char *SpillLibCall = getSpillLibCallName(*MF, CSI);
+  if (SpillLibCall) {
+    // Add spill libcall via non-callee-saved register t0.
+    BuildMI(MBB, MI, DL, TII.get(RISCV::PseudoCALLReg), RISCV::X5)
+        .addExternalSymbol(SpillLibCall, RISCVII::MO_CALL)
+        .setMIFlag(MachineInstr::FrameSetup);
+
+    // Add registers spilled in libcall as liveins.
+    for (auto &CS : CSI)
+      MBB.addLiveIn(CS.getReg());
+  }
+
+  // Manually spill values not spilled by libcall.
+  std::vector<CalleeSavedInfo> NonLibcallCSI = getNonLibcallCSI(CSI);
+  for (auto &CS : NonLibcallCSI) {
+    // Insert the spill to the stack frame.
+    unsigned Reg = CS.getReg();
+    const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg);
+    TII.storeRegToStackSlot(MBB, MI, Reg, true, CS.getFrameIdx(), RC, TRI);
+  }
+
+  return true;
+}
+
+bool RISCVFrameLowering::restoreCalleeSavedRegisters(
+    MachineBasicBlock &MBB, MachineBasicBlock::iterator MI,
+    std::vector<CalleeSavedInfo> &CSI, const TargetRegisterInfo *TRI) const {
+  if (CSI.empty())
+    return true;
+
+  MachineFunction *MF = MBB.getParent();
+  const TargetInstrInfo &TII = *MF->getSubtarget().getInstrInfo();
+  DebugLoc DL;
+  if (MI != MBB.end() && !MI->isDebugInstr())
+    DL = MI->getDebugLoc();
+
+  // Manually restore values not restored by libcall. Insert in reverse order.
+  // loadRegFromStackSlot can insert multiple instructions.
+  std::vector<CalleeSavedInfo> NonLibcallCSI = getNonLibcallCSI(CSI);
+  for (auto &CS : reverse(NonLibcallCSI)) {
+    unsigned Reg = CS.getReg();
+    const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg);
+    TII.loadRegFromStackSlot(MBB, MI, Reg, CS.getFrameIdx(), RC, TRI);
+    assert(MI != MBB.begin() && "loadRegFromStackSlot didn't insert any code!");
+  }
+
+  const char *RestoreLibCall = getRestoreLibCallName(*MF, CSI);
+  if (RestoreLibCall) {
+    // Replace terminating tail calls with a simple call. This is valid because
+    // the return address register is always callee saved as part of the
+    // save/restore libcalls.
+    if (MI != MBB.end() && MI->getOpcode() == RISCV::PseudoTAIL) {
+      MachineBasicBlock::iterator NewMI =
+          BuildMI(MBB, MI, DL, TII.get(RISCV::PseudoCALL))
+              .add(MI->getOperand(0));
+      NewMI->copyImplicitOps(*MF, *MI);
+      MI->eraseFromParent();
+      MI = ++NewMI;
+    }
+
+    // Add restore libcall via tail call.
+    MachineBasicBlock::iterator NewMI =
+        BuildMI(MBB, MI, DL, TII.get(RISCV::PseudoTAIL))
+            .addExternalSymbol(RestoreLibCall, RISCVII::MO_CALL)
+            .setMIFlag(MachineInstr::FrameDestroy);
+
+    // Remove trailing returns, since the terminator is now a tail call to the
+    // restore function.
+    if (MI != MBB.end() && MI->getOpcode() == RISCV::PseudoRET) {
+      NewMI->copyImplicitOps(*MF, *MI);
+      MI->eraseFromParent();
+    }
+  }
+
+  return true;
+}
Index: llvm/lib/Target/RISCV/RISCV.td
===================================================================
--- llvm/lib/Target/RISCV/RISCV.td
+++ llvm/lib/Target/RISCV/RISCV.td
@@ -69,6 +69,9 @@
     : SubtargetFeature<"relax", "EnableLinkerRelax", "true",
                        "Enable Linker relaxation.">;
 
+def FeatureSaveRestore : SubtargetFeature<"save-restore", "EnableSaveRestore",
+                                          "true", "Enable save/restore.">;
+
 //===----------------------------------------------------------------------===//
 // Named operands for CSR instructions.
 //===----------------------------------------------------------------------===//
Index: clang/test/Driver/riscv-features.c
===================================================================
--- clang/test/Driver/riscv-features.c
+++ clang/test/Driver/riscv-features.c
@@ -16,9 +16,10 @@
 // RUN: %clang -target riscv32-unknown-elf -### %s -msave-restore 2>&1 | FileCheck %s -check-prefix=SAVE-RESTORE
 // RUN: %clang -target riscv32-unknown-elf -### %s -mno-save-restore 2>&1 | FileCheck %s -check-prefix=NO-SAVE-RESTORE
 
-// SAVE-RESTORE: warning: the clang compiler does not support '-msave-restore'
-// NO-SAVE-RESTORE-NOT: warning: the clang compiler does not support
-// DEFAULT-NOT: warning: the clang compiler does not support
+// SAVE-RESTORE: "-target-feature" "+save-restore"
+// NO-SAVE-RESTORE: "-target-feature" "-save-restore"
+// DEFAULT: "-target-feature" "-save-restore"
+// DEFAULT-NOT: "-target-feature" "+save-restore"
 
 // RUN: %clang -target riscv32-linux -### %s -fsyntax-only 2>&1 \
 // RUN:   | FileCheck %s -check-prefix=DEFAULT-LINUX
Index: clang/lib/Driver/ToolChains/Arch/RISCV.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Arch/RISCV.cpp
+++ clang/lib/Driver/ToolChains/Arch/RISCV.cpp
@@ -374,12 +374,11 @@
     Features.push_back("-relax");
 
   // GCC Compatibility: -mno-save-restore is default, unless -msave-restore is
-  // specified...
-  if (Args.hasFlag(options::OPT_msave_restore, options::OPT_mno_save_restore, false)) {
-    // ... but we don't support -msave-restore, so issue a warning.
-    D.Diag(diag::warn_drv_clang_unsupported)
-      << Args.getLastArg(options::OPT_msave_restore)->getAsString(Args);
-  }
+  // specified.
+  if (Args.hasFlag(options::OPT_msave_restore, options::OPT_mno_save_restore, false))
+    Features.push_back("+save-restore");
+  else
+    Features.push_back("-save-restore");
 
   // Now add any that the user explicitly requested on the command line,
   // which may override the defaults.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to