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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits