blob: 881ce80ea763b3874a7ad030870c72f76fee52fe [file] [edit]
//===-- BPFUnwind.cpp - Lower unwinding to unwind_goto and unwind_exit ----===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// There are two ways to leave a frame, and a call that may unwind is followed
// by an insn that tells them apart:
// ret exit
// invoke f() unwind pad call f; unwind_goto pad
// resume r0 = 0; unwind_exit
// call _Unwind_RaiseException r0 = 0; unwind_exit
// invoke _Unwind_RaiseException goto pad
// unwind_goto is taken when the callee left with unwind_exit. No register says
// so: how one insn tells the other is up to the JIT. Otherwise unwind_exit is
// exit, hence r0, and r2 as well in a function that returns more than 8 bytes.
// A pad expects the exception object in r0. There is none, so a pad that looks
// at it starts with r0 = 0.
//
// _Unwind_RaiseException() is how a language runtime starts an unwind with the
// Itanium ABI, e.g. at the end of a panic handler. To start one and to carry
// one on is the same thing here: leave the frame with unwind_exit, after its
// own pad if it has one. It cannot fail, so it does not return.
//
// There are no tables and nothing for a loader to do.
//
//===----------------------------------------------------------------------===//
#include "BPF.h"
#include "BPFSubtarget.h"
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/CodeGen/LivePhysRegs.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
#include "llvm/Transforms/Utils/Local.h"
using namespace llvm;
/// On other targets it is a function of the unwinder library, which is why
/// nothing else in LLVM knows the name.
static constexpr StringLiteral UnwindRaise = "_Unwind_RaiseException";
/// Not being nounwind is the default in IR, so that alone is not enough:
/// uwtable is how a frontend asks for unwinding through a function to work.
static bool canUnwind(const Function &F) {
return F.hasUWTable() && !F.doesNotThrow();
}
namespace {
/// Make every call that may unwind an invoke: it ends its block and has a pad.
struct BPFUnwindPrepare : public ModulePass {
static char ID;
BPFUnwindPrepare() : ModulePass(ID) {}
StringRef getPassName() const override { return "BPF Unwind Prepare"; }
static bool isRaise(const CallBase &CB) {
const auto *F = dyn_cast<Function>(CB.getCalledOperand());
return F && F->isDeclaration() && F->getName() == UnwindRaise;
}
static bool mayUnwind(const Instruction &I) {
const auto *CB = dyn_cast<CallBase>(&I);
return CB && !CB->doesNotThrow() && !CB->isInlineAsm() &&
!isa<IntrinsicInst>(CB);
}
bool runOnModule(Module &M) override {
bool Changed = false;
for (Function &F : M) {
if (F.isDeclaration() || !canUnwind(F))
continue;
LLVMContext &C = M.getContext();
SmallVector<CallInst *, 8> Calls;
for (Instruction &I : instructions(F)) {
auto *CB = dyn_cast<CallBase>(&I);
if (CB && isRaise(*CB)) {
// Nobody looks at the exception object.
for (Use &Arg : CB->args())
Arg.set(PoisonValue::get(Arg->getType()));
CB->setDoesNotReturn();
// Without a pad it leaves the frame.
if (isa<CallInst>(CB))
CB->setDoesNotThrow();
Changed = true;
} else if (isa<CallInst>(I) && mayUnwind(I)) {
Calls.push_back(cast<CallInst>(&I));
} else if (auto *RI = dyn_cast<ResumeInst>(&I)) {
// DwarfEHPrepare passes it to _Unwind_Resume(). Don't keep it alive
// over the pad for that.
RI->setOperand(0, PoisonValue::get(RI->getValue()->getType()));
Changed = true;
}
}
if (Calls.empty())
continue;
if (!F.hasPersonalityFn())
F.setPersonalityFn(cast<Constant>(
M.getOrInsertFunction("__gcc_personality_v0",
FunctionType::get(Type::getInt32Ty(C), true))
.getCallee()));
BasicBlock *Pad = BasicBlock::Create(C, "unwind.resume", &F);
IRBuilder<> B(Pad);
LandingPadInst *LP = B.CreateLandingPad(
StructType::get(PointerType::getUnqual(C), Type::getInt32Ty(C)), 0);
LP->setCleanup(true);
B.CreateResume(PoisonValue::get(LP->getType()));
for (CallInst *CI : Calls)
changeToInvokeAndSplitBasicBlock(CI, Pad);
Changed = true;
}
return Changed;
}
};
/// After register allocation, which took call -> pad for an EH edge and so
/// keeps only r6-r9 and the stack alive over it, turn the edge into a branch.
struct BPFUnwindLower : public MachineFunctionPass {
static char ID;
BPFUnwindLower() : MachineFunctionPass(ID) {}
StringRef getPassName() const override { return "BPF Unwind Lower"; }
/// What DwarfEHPrepare made of 'resume'.
StringRef UnwindResume;
bool leavesFrame(const MachineInstr &MI) const {
const MachineOperand &MO = MI.getOperand(0);
// A program that defines them gets what it defines.
if (!MO.isGlobal() || !MO.getGlobal()->isDeclaration())
return false;
StringRef Name = MO.getGlobal()->getName();
return Name == UnwindRaise || Name == UnwindResume;
}
/// Nothing after \p MI in its block runs: \p Succ, if any, is what follows.
static void endBlockAfter(MachineInstr &MI, MachineBasicBlock *Succ) {
MachineBasicBlock &MBB = *MI.getParent();
MBB.erase(std::next(MI.getIterator()), MBB.end());
while (!MBB.succ_empty())
MBB.removeSuccessor(MBB.succ_begin());
if (Succ)
MBB.addSuccessor(Succ);
}
/// The argument of a call that goes away is poison, which is 'r1 = 0'.
/// Nothing reads r1 after a call.
static void eraseArg(MachineInstr &Call) {
MachineBasicBlock::iterator I = Call, Begin = Call.getParent()->begin();
if (I == Begin)
return;
I = prev_nodbg(I, Begin);
if (I->getOpcode() == BPF::MOV_ri && I->getOperand(0).getReg() == BPF::R1)
I->eraseFromParent();
}
static const char *unsupported(const MachineFunction &MF) {
if (MF.getLandingPads().empty())
return nullptr;
// Not canUnwind(): a function that catches everything is nounwind.
if (!MF.getFunction().hasUWTable())
return "BPF needs uwtable on a function with a landing pad";
// Every pad is a catch-all: the branch is taken whatever was thrown.
for (const LandingPadInfo &LP : MF.getLandingPads()) {
for (int TId : LP.TypeIds) {
if (TId > 0 && MF.getTypeInfos()[TId - 1])
return "BPF does not support type-specific exception catches yet";
// An empty filter lets nothing through, so it is a catch-all too. rustc
// guards every function that must not unwind with one.
if (TId < 0 && MF.getFilterIds()[-1 - TId])
return "BPF does not support exception filters yet";
}
}
return nullptr;
}
bool runOnMachineFunction(MachineFunction &MF) override {
const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo();
const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
UnwindResume = MF.getSubtarget().getTargetLowering()->getLibcallName(
RTLIB::UNWIND_RESUME);
DenseMap<MCSymbol *, MachineBasicBlock *> Begin;
SmallVector<std::pair<MachineInstr *, MachineBasicBlock *>, 8> Sites;
SmallVector<MachineInstr *, 8> Labels, Resumes;
SmallSetVector<MachineBasicBlock *, 8> Pads;
if (const char *Msg = unsupported(MF)) {
const Function &F = MF.getFunction();
F.getContext().diagnose(DiagnosticInfoUnsupported(F, Msg));
return false;
}
for (const LandingPadInfo &LP : MF.getLandingPads())
for (MCSymbol *Sym : LP.BeginLabels)
Begin[Sym] = LP.LandingPadBlock;
// The call is not always in the block of the label: an argument that is
// a select is computed after the label and takes blocks of its own.
SmallPtrSet<MachineInstr *, 8> Invokes;
for (MachineBasicBlock &MBB : MF) {
for (MachineInstr &Label : MBB) {
if (!Label.isEHLabel())
continue;
Labels.push_back(&Label);
MachineBasicBlock *Pad =
Begin.lookup(Label.getOperand(0).getMCSymbol());
if (!Pad)
continue;
SmallVector<std::pair<MachineBasicBlock *, MachineBasicBlock::iterator>,
4>
Work{{&MBB, std::next(Label.getIterator())}};
SmallPtrSet<MachineBasicBlock *, 4> Seen;
while (!Work.empty()) {
auto [Cur, I] = Work.pop_back_val();
// The next label ends the range.
for (; I != Cur->end() && !I->isEHLabel(); ++I)
if (I->isCall() && Invokes.insert(&*I).second)
Sites.push_back({&*I, Pad});
if (I != Cur->end())
continue;
for (MachineBasicBlock *Succ : Cur->successors())
if (!Succ->isEHPad() && Seen.insert(Succ).second)
Work.push_back({Succ, Succ->begin()});
}
}
}
for (MachineBasicBlock &MBB : MF)
for (MachineInstr &MI : MBB)
if (MI.isCall() && !Invokes.count(&MI) && leavesFrame(MI))
Resumes.push_back(&MI);
if (Labels.empty() && Resumes.empty())
return false;
// There is no unwind_goto after the calls of it.
if (!Resumes.empty() && !canUnwind(MF.getFunction())) {
const Function &F = MF.getFunction();
F.getContext().diagnose(DiagnosticInfoUnsupported(
F, "BPF cannot unwind out of a function that is nounwind or has no "
"uwtable"));
return false;
}
for (MachineInstr *MI : Labels)
MI->eraseFromParent();
for (auto [Call, Pad] : Sites) {
MachineBasicBlock *MBB = Call->getParent();
DebugLoc DL = Call->getDebugLoc();
const auto *II =
dyn_cast_or_null<InvokeInst>(MBB->getBasicBlock()->getTerminator());
Pads.insert(Pad);
if (leavesFrame(*Call)) {
endBlockAfter(*Call, Pad);
BuildMI(MBB, DL, TII.get(BPF::JMP)).addMBB(Pad);
eraseArg(*Call);
Call->eraseFromParent();
} else if (II && II->doesNotReturn()) {
// Nothing sensible follows the call, but something has to.
endBlockAfter(*Call, Pad);
BuildMI(MBB, DL, TII.get(BPF::UNWIND_GOTO)).addMBB(Pad);
BuildMI(MBB, DL, TII.get(BPF::JMP)).addMBB(Pad);
} else {
MachineBasicBlock *Rest = MBB->splitAt(*Call);
if (Rest != MBB) {
Rest->removeSuccessor(Pad);
MBB->addSuccessor(Pad);
}
BuildMI(MBB, DL, TII.get(BPF::UNWIND_GOTO)).addMBB(Pad);
}
}
for (MachineBasicBlock *Pad : Pads) {
LivePhysRegs Live(TRI);
Live.addLiveOuts(*Pad);
for (MachineInstr &MI : reverse(*Pad))
Live.stepBackward(MI);
if (!Live.available(MF.getRegInfo(), BPF::R0))
BuildMI(*Pad, Pad->begin(), DebugLoc(), TII.get(BPF::MOV_ri), BPF::R0)
.addImm(0);
Pad->removeLiveIn(BPF::R0);
}
// unwind_exit returns what exit returns: a value of more than 8 bytes is in
// r0 and r2.
SmallVector<ISD::OutputArg, 2> Outs;
const Function &F = MF.getFunction();
GetReturnInfo(F.getCallingConv(), F.getReturnType(), F.getAttributes(),
Outs, *MF.getSubtarget().getTargetLowering(),
MF.getDataLayout());
for (MachineInstr *Call : Resumes) {
MachineBasicBlock *MBB = Call->getParent();
DebugLoc DL = Call->getDebugLoc();
eraseArg(*Call);
BuildMI(*MBB, Call, DL, TII.get(BPF::MOV_ri), BPF::R0).addImm(0);
MachineInstrBuilder Exit =
BuildMI(*MBB, Call, DL, TII.get(BPF::UNWIND_EXIT))
.addReg(BPF::R0, RegState::Implicit);
if (Outs.size() > 1) {
BuildMI(*MBB, *Exit, DL, TII.get(BPF::MOV_ri), BPF::R2).addImm(0);
Exit.addReg(BPF::R2, RegState::Implicit);
}
endBlockAfter(*Exit, nullptr);
}
// The pads are plain blocks now and there is no table to emit.
for (MachineBasicBlock &MBB : MF)
MBB.setIsEHPad(false);
MF.clearLandingPads();
// What was behind a call that does not return. It can be a loop.
df_iterator_default_set<MachineBasicBlock *> Reachable;
for (MachineBasicBlock *MBB : depth_first_ext(&MF, Reachable))
(void)MBB;
for (MachineBasicBlock &MBB : make_early_inc_range(MF)) {
if (Reachable.count(&MBB) || MBB.hasAddressTaken())
continue;
while (!MBB.succ_empty())
MBB.removeSuccessor(MBB.succ_begin());
MBB.eraseFromParent();
}
for (MachineBasicBlock &MBB : MF)
if (!MBB.empty() && MBB.back().getOpcode() == BPF::JMP &&
MBB.isLayoutSuccessor(MBB.back().getOperand(0).getMBB()))
MBB.back().eraseFromParent();
return true;
}
};
} // namespace
char BPFUnwindPrepare::ID = 0;
char BPFUnwindLower::ID = 0;
ModulePass *llvm::createBPFUnwindPreparePass() {
return new BPFUnwindPrepare();
}
FunctionPass *llvm::createBPFUnwindLowerPass() { return new BPFUnwindLower(); }