lewis-revill updated this revision to Diff 222612.
lewis-revill added a comment.

Rewrote logic to calculate stack sizes, frame indexes and frame pointer 
offsets. This was necessary to take into account the fact that the save/restore 
lib calls are essentially an opaque section of the stack that is inserted at 
the beginning of the frame, after positive offset objects such as fixed stack 
arguments, but before additional callee saved registers (IE FP registers) and 
negative offset (dynamic) objects. So calculations of the actual offsets of 
these objects must be adjusted accordingly for the stack to function correctly.


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:    sw a1, 20(sp)
+; RV32I-NEXT:    addi a1, sp, 24
+; RV32I-NEXT:    sw a1, 12(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:    sw a1, 20(sp)
+; RV32I-SR-NEXT:    addi a1, sp, 24
+; RV32I-SR-NEXT:    sw a1, 12(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:    sw a1, 20(sp)
+; RV32I-FP-SR-NEXT:    addi a1, sp, 24
+; RV32I-FP-SR-NEXT:    sw a1, 12(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:    mv s1, sp
+; RV64I-NEXT:    slli a0, a0, 32
+; RV64I-NEXT:    srli a0, a0, 32
+; RV64I-NEXT:    addi a0, a0, 15
+; RV64I-NEXT:    addi a1, zero, 1
+; RV64I-NEXT:    slli a1, a1, 33
+; RV64I-NEXT:    addi a1, a1, -16
+; RV64I-NEXT:    and a0, a0, a1
+; 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:    addi s0, sp, 16
+; 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:    addi sp, s0, -16
+; 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:    addi s0, sp, 32
+; RV64I-SR-NEXT:    mv s1, sp
+; RV64I-SR-NEXT:    slli a0, a0, 32
+; RV64I-SR-NEXT:    srli a0, a0, 32
+; RV64I-SR-NEXT:    addi a0, a0, 15
+; RV64I-SR-NEXT:    addi a1, zero, 1
+; RV64I-SR-NEXT:    slli a1, a1, 33
+; RV64I-SR-NEXT:    addi a1, a1, -16
+; RV64I-SR-NEXT:    and a0, a0, a1
+; 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:    addi sp, s0, -32
+; 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:    addi s0, sp, 16
+; 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:    addi sp, s0, -16
+; 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:    addi s0, sp, 32
+; RV64I-FP-SR-NEXT:    mv s1, sp
+; 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:    addi a1, zero, 1
+; RV64I-FP-SR-NEXT:    slli a1, a1, 33
+; RV64I-FP-SR-NEXT:    addi a1, a1, -16
+; RV64I-FP-SR-NEXT:    and a0, a0, a1
+; 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:    addi sp, s0, -32
+; 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"
@@ -89,6 +90,39 @@
   return CSR_NoRegs_RegMask;
 }
 
+// Frame indexes representing locations of CSRs which are given a fixed location
+// by save/restore libcalls.
+static const 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"
 
@@ -30,6 +31,8 @@
   /// FrameIndex used for transferring values between 64-bit FPRs and a pair
   /// of 32-bit GPRs via the stack.
   int MoveF64FrameIndex = -1;
+  /// Size of any opaque stack adjustment due to save/restore libcalls.
+  unsigned LibCallStackSize = 0;
 
 public:
   RISCVMachineFunctionInfo(MachineFunction &MF) : MF(MF) {}
@@ -45,6 +48,16 @@
       MoveF64FrameIndex = MF.getFrameInfo().CreateStackObject(8, 8, false);
     return MoveF64FrameIndex;
   }
+
+  unsigned getLibCallStackSize() const { return LibCallStackSize; }
+  void setLibCallStackSize(unsigned Size) { LibCallStackSize = Size; }
+
+  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
@@ -22,6 +22,96 @@
 
 using namespace llvm;
 
+// Get the ID of the libcall used for spilling and restoring callee saved
+// registers.
+static int getLibCallID(const MachineFunction &MF,
+                        const std::vector<CalleeSavedInfo> &CSI) {
+  const auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
+
+  if (CSI.empty() || !RVFI->useSaveRestoreLibCalls())
+    return -1;
+
+  unsigned MaxReg = 0;
+  for (auto &CS : CSI)
+    if (CS.getFrameIdx() < 0)
+      MaxReg = std::max(MaxReg, CS.getReg());
+
+  if (MaxReg == 0)
+    return -1;
+
+  switch (MaxReg) {
+  default:
+    llvm_unreachable("Something has gone wrong!");
+  case /*s11*/ RISCV::X27: return 12;
+  case /*s10*/ RISCV::X26: return 11;
+  case /*s9*/  RISCV::X25: return 10;
+  case /*s8*/  RISCV::X24: return 9;
+  case /*s7*/  RISCV::X23: return 8;
+  case /*s6*/  RISCV::X22: return 7;
+  case /*s5*/  RISCV::X21: return 6;
+  case /*s4*/  RISCV::X20: return 5;
+  case /*s3*/  RISCV::X19: return 4;
+  case /*s2*/  RISCV::X18: return 3;
+  case /*s1*/  RISCV::X9:  return 2;
+  case /*s0*/  RISCV::X8:  return 1;
+  case /*ra*/  RISCV::X1:  return 0;
+  }
+}
+
+// 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(const MachineFunction &MF,
+                    const std::vector<CalleeSavedInfo> &CSI) {
+  static const char *const spillLibCalls[] = {
+    "__riscv_save_0",
+    "__riscv_save_1",
+    "__riscv_save_2",
+    "__riscv_save_3",
+    "__riscv_save_4",
+    "__riscv_save_5",
+    "__riscv_save_6",
+    "__riscv_save_7",
+    "__riscv_save_8",
+    "__riscv_save_9",
+    "__riscv_save_10",
+    "__riscv_save_11",
+    "__riscv_save_12"
+  };
+
+  int libCallID = getLibCallID(MF, CSI);
+  if (libCallID == -1)
+    return nullptr;
+  return spillLibCalls[libCallID];
+}
+
+// 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(const MachineFunction &MF,
+                      const std::vector<CalleeSavedInfo> &CSI) {
+  static const char *const restoreLibCalls[] = {
+    "__riscv_restore_0",
+    "__riscv_restore_1",
+    "__riscv_restore_2",
+    "__riscv_restore_3",
+    "__riscv_restore_4",
+    "__riscv_restore_5",
+    "__riscv_restore_6",
+    "__riscv_restore_7",
+    "__riscv_restore_8",
+    "__riscv_restore_9",
+    "__riscv_restore_10",
+    "__riscv_restore_11",
+    "__riscv_restore_12"
+  };
+
+  int libCallID = getLibCallID(MF, CSI);
+  if (libCallID == -1)
+    return nullptr;
+  return restoreLibCalls[libCallID];
+}
+
 bool RISCVFrameLowering::hasFP(const MachineFunction &MF) const {
   const TargetRegisterInfo *RegInfo = MF.getSubtarget().getRegisterInfo();
 
@@ -97,6 +187,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 +217,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;
@@ -123,12 +229,38 @@
   // Determine the correct frame layout
   determineFrameLayout(MF);
 
+  // If libcalls are used to spill and restore callee-saved registers, the frame
+  // has two sections; the opaque section managed by the libcalls, and the
+  // section managed by MachineFrameInfo which can also hold callee saved
+  // registers in fixed stack slots, both of which have negative frame indices.
+  // This gets even more complicated when incoming arguments are passed via the
+  // stack, as these too have negative frame indices. An example is detailed
+  // below:
+  //
+  //  | incoming arg | <- FI[-3]
+  //  | libcallspill |
+  //  | calleespill  | <- FI[-2]
+  //  | calleespill  | <- FI[-1]
+  //  | this_frame   | <- FI[0]
+  //
+  // For negative frame indices, the offset from the frame pointer will differ
+  // depending on which of these groups the frame index applies to.
+  // The following calculates the correct offset knowing the number of callee
+  // saved registers spilt by the two methods.
+  if (int LibCallRegs = getLibCallID(MF, MFI.getCalleeSavedInfo()) + 1) {
+    // Calculate the size of the frame managed by the libcall. The libcalls are
+    // implemented such that the stack will always be 16 byte aligned.
+    unsigned LibCallFrameSize = alignTo((STI.getXLen() / 8) * LibCallRegs, 16);
+    RVFI->setLibCallStackSize(LibCallFrameSize);
+  }
+
   // FIXME (note copied from Lanai): This appears to be overallocating.  Needs
   // investigation. Get the number of bytes to allocate from the FrameInfo.
   uint64_t StackSize = MFI.getStackSize();
+  uint64_t RealStackSize = StackSize + RVFI->getLibCallStackSize();
 
   // Early exit if there is no need to allocate on the stack
-  if (StackSize == 0 && !MFI.adjustsStack())
+  if (RealStackSize == 0 && !MFI.adjustsStack())
     return;
 
   // Allocate space on the stack if necessary.
@@ -140,30 +272,34 @@
   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.
   if (hasFP(MF)) {
     adjustReg(MBB, MBBI, DL, FPReg, SPReg,
-              StackSize - RVFI->getVarArgsSaveSize(), MachineInstr::FrameSetup);
+              RealStackSize - RVFI->getVarArgsSaveSize(),
+              MachineInstr::FrameSetup);
 
     // Emit ".cfi_def_cfa $fp, 0"
     unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createDefCfa(
@@ -207,13 +343,25 @@
   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();
+  uint64_t RealStackSize = StackSize + RVFI->getLibCallStackSize();
+  uint64_t FPOffset = RealStackSize - RVFI->getVarArgsSaveSize();
 
   // Restore the stack pointer using the value of the frame pointer. Only
   // necessary if the stack pointer was modified, meaning the stack size is
@@ -245,15 +393,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
@@ -272,12 +421,13 @@
                                                unsigned &FrameReg) const {
   const MachineFrameInfo &MFI = MF.getFrameInfo();
   const TargetRegisterInfo *RI = MF.getSubtarget().getRegisterInfo();
-  const auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
+  auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
 
   // Callee-saved registers should be referenced relative to the stack
   // pointer (positive offset), otherwise use the frame pointer (negative
   // offset).
-  const std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo();
+  const std::vector<CalleeSavedInfo> &CSI =
+      getNonLibcallCSI(MFI.getCalleeSavedInfo());
   int MinCSFI = 0;
   int MaxCSFI = -1;
 
@@ -291,20 +441,27 @@
 
   if (FI >= MinCSFI && FI <= MaxCSFI) {
     FrameReg = RISCV::X2;
-    Offset += MF.getFrameInfo().getStackSize();
+    Offset += MFI.getStackSize();
   } else if (RI->needsStackRealignment(MF)) {
     assert(!MFI.hasVarSizedObjects() &&
            "Unexpected combination of stack realignment and varsized objects");
     // If the stack was realigned, the frame pointer is set in order to allow
     // SP to be restored, but we still access stack objects using SP.
     FrameReg = RISCV::X2;
-    Offset += MF.getFrameInfo().getStackSize();
+    Offset += MFI.getStackSize();
+    if (FI < 0)
+      Offset += RVFI->getLibCallStackSize();
   } else {
     FrameReg = RI->getFrameRegister(MF);
-    if (hasFP(MF))
+    if (hasFP(MF)) {
       Offset += RVFI->getVarArgsSaveSize();
-    else
-      Offset += MF.getFrameInfo().getStackSize();
+      if (FI >= 0)
+        Offset -= RVFI->getLibCallStackSize();
+    } else {
+      Offset += MFI.getStackSize();
+      if (FI < 0)
+        Offset += RVFI->getLibCallStackSize();
+    }
   }
   return Offset;
 }
@@ -404,3 +561,93 @@
 
   return MBB.erase(MI);
 }
+
+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