llvm-project/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.cpp
Sam Elliott cfc5baf6e6
[RISCV] SiFive CLIC Support (#132481)
This Change adds support for two SiFive vendor attributes in clang:
- "SiFive-CLIC-preemptible"
- "SiFive-CLIC-stack-swap"

These can be given together, and can be combined with "machine", but
cannot be combined with any other interrupt attribute values.

These are handled primarily in RISCVFrameLowering:
- "SiFive-CLIC-stack-swap" entails swapping `sp` with `sf.mscratchcsw`
  at function entry and exit, which holds the trap stack pointer.
- "SiFive-CLIC-preemptible" entails saving `mcause` and `mepc` before
  re-enabling interrupts using `mstatus`. To save these, `s0` and `s1`
  are first spilled to the stack, and then the values are read into
  these registers. If these registers are used in the function, their
  values will be spilled a second time onto the stack with the generic
  callee-saved-register handling. At the end of the function interrupts
  are disabled again before `mepc` and `mcause` are restored.

This Change also adds support for the following two experimental
extensions, which only contain CSRs:
- XSfsclic - for SiFive's CLIC Supervisor-Mode CSRs
- XSfmclic - for SiFive's CLIC Machine-Mode CSRs

The latter is needed for interrupt support.

The CFI information for this implementation is not correct, but I'd
prefer to correct this in a follow-up. While it's unlikely anyone wants
to unwind through a handler, the CFI information is also used by
debuggers so it would be good to get it right.

Co-authored-by: Ana Pazos <apazos@quicinc.com>
2025-04-25 17:12:27 -07:00

148 lines
5.2 KiB
C++

//=- RISCVMachineFunctionInfo.cpp - RISC-V machine function info --*- C++ -*-=//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file declares RISCV-specific per-machine-function information.
//
//===----------------------------------------------------------------------===//
#include "RISCVMachineFunctionInfo.h"
#include "llvm/IR/Module.h"
using namespace llvm;
yaml::RISCVMachineFunctionInfo::RISCVMachineFunctionInfo(
const llvm::RISCVMachineFunctionInfo &MFI)
: VarArgsFrameIndex(MFI.getVarArgsFrameIndex()),
VarArgsSaveSize(MFI.getVarArgsSaveSize()) {}
MachineFunctionInfo *RISCVMachineFunctionInfo::clone(
BumpPtrAllocator &Allocator, MachineFunction &DestMF,
const DenseMap<MachineBasicBlock *, MachineBasicBlock *> &Src2DstMBB)
const {
return DestMF.cloneInfo<RISCVMachineFunctionInfo>(*this);
}
RISCVMachineFunctionInfo::RISCVMachineFunctionInfo(const Function &F,
const RISCVSubtarget *STI) {
// The default stack probe size is 4096 if the function has no
// stack-probe-size attribute. This is a safe default because it is the
// smallest possible guard page size.
uint64_t ProbeSize = 4096;
if (F.hasFnAttribute("stack-probe-size"))
ProbeSize = F.getFnAttributeAsParsedInteger("stack-probe-size");
else if (const auto *PS = mdconst::extract_or_null<ConstantInt>(
F.getParent()->getModuleFlag("stack-probe-size")))
ProbeSize = PS->getZExtValue();
assert(int64_t(ProbeSize) > 0 && "Invalid stack probe size");
// Round down to the stack alignment.
uint64_t StackAlign =
STI->getFrameLowering()->getTransientStackAlign().value();
ProbeSize = std::max(StackAlign, alignDown(ProbeSize, StackAlign));
StringRef ProbeKind;
if (F.hasFnAttribute("probe-stack"))
ProbeKind = F.getFnAttribute("probe-stack").getValueAsString();
else if (const auto *PS = dyn_cast_or_null<MDString>(
F.getParent()->getModuleFlag("probe-stack")))
ProbeKind = PS->getString();
if (ProbeKind.size()) {
StackProbeSize = ProbeSize;
}
}
RISCVMachineFunctionInfo::InterruptStackKind
RISCVMachineFunctionInfo::getInterruptStackKind(
const MachineFunction &MF) const {
if (!MF.getFunction().hasFnAttribute("interrupt"))
return InterruptStackKind::None;
assert(VarArgsSaveSize == 0 &&
"Interrupt functions should not having incoming varargs");
StringRef InterruptVal =
MF.getFunction().getFnAttribute("interrupt").getValueAsString();
return StringSwitch<RISCVMachineFunctionInfo::InterruptStackKind>(
InterruptVal)
.Case("qci-nest", InterruptStackKind::QCINest)
.Case("qci-nonest", InterruptStackKind::QCINoNest)
.Case("SiFive-CLIC-preemptible",
InterruptStackKind::SiFiveCLICPreemptible)
.Case("SiFive-CLIC-stack-swap", InterruptStackKind::SiFiveCLICStackSwap)
.Case("SiFive-CLIC-preemptible-stack-swap",
InterruptStackKind::SiFiveCLICPreemptibleStackSwap)
.Default(InterruptStackKind::None);
}
void yaml::RISCVMachineFunctionInfo::mappingImpl(yaml::IO &YamlIO) {
MappingTraits<RISCVMachineFunctionInfo>::mapping(YamlIO, *this);
}
RISCVMachineFunctionInfo::PushPopKind
RISCVMachineFunctionInfo::getPushPopKind(const MachineFunction &MF) const {
// We cannot use fixed locations for the callee saved spill slots if the
// function uses a varargs save area.
// TODO: Use a separate placement for vararg registers to enable Zcmp.
if (VarArgsSaveSize != 0)
return PushPopKind::None;
// SiFive interrupts are not compatible with push/pop.
if (useSiFiveInterrupt(MF))
return PushPopKind::None;
// Zcmp is not compatible with the frame pointer convention.
if (MF.getSubtarget<RISCVSubtarget>().hasStdExtZcmp() &&
!MF.getTarget().Options.DisableFramePointerElim(MF))
return PushPopKind::StdExtZcmp;
// Xqccmp is Zcmp but has a push order compatible with the frame-pointer
// convention.
if (MF.getSubtarget<RISCVSubtarget>().hasVendorXqccmp())
return PushPopKind::VendorXqccmp;
return PushPopKind::None;
}
bool RISCVMachineFunctionInfo::hasImplicitFPUpdates(
const MachineFunction &MF) const {
switch (getInterruptStackKind(MF)) {
case InterruptStackKind::QCINest:
case InterruptStackKind::QCINoNest:
// QC.C.MIENTER and QC.C.MIENTER.NEST both update FP on function entry.
return true;
default:
break;
}
switch (getPushPopKind(MF)) {
case PushPopKind::VendorXqccmp:
// When using Xqccmp, we will use `QC.CM.PUSHFP` when Frame Pointers are
// enabled, which will update FP.
return true;
default:
break;
}
return false;
}
void RISCVMachineFunctionInfo::initializeBaseYamlFields(
const yaml::RISCVMachineFunctionInfo &YamlMFI) {
VarArgsFrameIndex = YamlMFI.VarArgsFrameIndex;
VarArgsSaveSize = YamlMFI.VarArgsSaveSize;
}
void RISCVMachineFunctionInfo::addSExt32Register(Register Reg) {
SExt32Registers.push_back(Reg);
}
bool RISCVMachineFunctionInfo::isSExt32Register(Register Reg) const {
return is_contained(SExt32Registers, Reg);
}