1//===-- BPFAsmPrinter.cpp - BPF LLVM assembly writer ----------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file contains a printer that converts from our internal representation
10// of machine-dependent LLVM code to the BPF assembly language.
11//
12//===----------------------------------------------------------------------===//
13
14#include "BPFAsmPrinter.h"
15#include "BPF.h"
16#include "BPFInstrInfo.h"
17#include "BPFMCInstLower.h"
18#include "BTFDebug.h"
19#include "MCTargetDesc/BPFInstPrinter.h"
20#include "TargetInfo/BPFTargetInfo.h"
21#include "llvm/BinaryFormat/ELF.h"
22#include "llvm/CodeGen/AsmPrinter.h"
23#include "llvm/CodeGen/AsmPrinterAnalysis.h"
24#include "llvm/CodeGen/MachineConstantPool.h"
25#include "llvm/CodeGen/MachineFunction.h"
26#include "llvm/CodeGen/MachineFunctionAnalysisManager.h"
27#include "llvm/CodeGen/MachineInstr.h"
28#include "llvm/CodeGen/MachineJumpTableInfo.h"
29#include "llvm/CodeGen/MachineModuleInfo.h"
30#include "llvm/CodeGen/MachinePassManager.h"
31#include "llvm/CodeGen/TargetLowering.h"
32#include "llvm/IR/Analysis.h"
33#include "llvm/IR/DiagnosticInfo.h"
34#include "llvm/IR/Module.h"
35#include "llvm/IR/PassManager.h"
36#include "llvm/MC/MCAsmInfo.h"
37#include "llvm/MC/MCExpr.h"
38#include "llvm/MC/MCInst.h"
39#include "llvm/MC/MCSectionELF.h"
40#include "llvm/MC/MCStreamer.h"
41#include "llvm/MC/MCSymbol.h"
42#include "llvm/MC/MCSymbolELF.h"
43#include "llvm/MC/TargetRegistry.h"
44#include "llvm/Support/Compiler.h"
45#include "llvm/Support/raw_ostream.h"
46#include "llvm/Target/TargetLoweringObjectFile.h"
47using namespace llvm;
48
49#define DEBUG_TYPE "asm-printer"
50
51BPFAsmPrinter::BPFAsmPrinter(TargetMachine &TM,
52 std::unique_ptr<MCStreamer> Streamer)
53 : AsmPrinter(TM, std::move(Streamer), ID), BTF(nullptr), TM(TM) {}
54
55BPFAsmPrinter::~BPFAsmPrinter() = default;
56
57bool BPFAsmPrinter::doInitialization(Module &M) {
58 AsmPrinter::doInitialization(M);
59
60 // Only emit BTF when debuginfo available.
61 if (MAI.doesSupportDebugInformation() && !M.debug_compile_units().empty()) {
62 BTF = new BTFDebug(this);
63 Handlers.push_back(Elt: std::unique_ptr<BTFDebug>(BTF));
64 }
65
66 return false;
67}
68
69const BPFTargetMachine &BPFAsmPrinter::getBTM() const {
70 return static_cast<const BPFTargetMachine &>(TM);
71}
72
73bool BPFAsmPrinter::doFinalization(Module &M) {
74 // Remove unused globals which are previously used for jump table.
75 const BPFSubtarget *Subtarget = getBTM().getSubtargetImpl();
76 if (Subtarget->hasGotox()) {
77 std::vector<GlobalVariable *> Targets;
78 for (GlobalVariable &Global : M.globals()) {
79 if (Global.getLinkage() != GlobalValue::PrivateLinkage)
80 continue;
81 if (!Global.isConstant() || !Global.hasInitializer())
82 continue;
83
84 Constant *CV = dyn_cast<Constant>(Val: Global.getInitializer());
85 if (!CV)
86 continue;
87 ConstantArray *CA = dyn_cast<ConstantArray>(Val: CV);
88 if (!CA)
89 continue;
90
91 if (!all_of(Range: CA->operands(),
92 P: [](const Use &Op) { return isa<BlockAddress>(Val: Op); }))
93 continue;
94 Targets.push_back(x: &Global);
95 }
96
97 for (GlobalVariable *GV : Targets) {
98 GV->replaceAllUsesWith(V: PoisonValue::get(T: GV->getType()));
99 GV->dropAllReferences();
100 GV->eraseFromParent();
101 }
102 }
103
104 for (GlobalObject &GO : M.global_objects()) {
105 if (!GO.hasExternalWeakLinkage())
106 continue;
107
108 if (!SawTrapCall && GO.getName() == BPF_TRAP) {
109 GO.eraseFromParent();
110 break;
111 }
112 }
113
114 return AsmPrinter::doFinalization(M);
115}
116
117void BPFAsmPrinter::printOperand(const MachineInstr *MI, int OpNum,
118 raw_ostream &O) {
119 const MachineOperand &MO = MI->getOperand(i: OpNum);
120
121 switch (MO.getType()) {
122 case MachineOperand::MO_Register:
123 O << BPFInstPrinter::getRegisterName(Reg: MO.getReg());
124 break;
125
126 case MachineOperand::MO_Immediate:
127 O << MO.getImm();
128 break;
129
130 case MachineOperand::MO_MachineBasicBlock:
131 O << *MO.getMBB()->getSymbol();
132 break;
133
134 case MachineOperand::MO_GlobalAddress:
135 O << *getSymbol(GV: MO.getGlobal());
136 break;
137
138 case MachineOperand::MO_BlockAddress: {
139 MCSymbol *BA = GetBlockAddressSymbol(BA: MO.getBlockAddress());
140 O << BA->getName();
141 break;
142 }
143
144 case MachineOperand::MO_ExternalSymbol:
145 O << *GetExternalSymbolSymbol(Sym: MO.getSymbolName());
146 break;
147
148 case MachineOperand::MO_JumpTableIndex:
149 case MachineOperand::MO_ConstantPoolIndex:
150 default:
151 llvm_unreachable("<unknown operand type>");
152 }
153}
154
155bool BPFAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
156 const char *ExtraCode, raw_ostream &O) {
157 if (ExtraCode && ExtraCode[0])
158 return AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS&: O);
159
160 printOperand(MI, OpNum: OpNo, O);
161 return false;
162}
163
164bool BPFAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
165 unsigned OpNum, const char *ExtraCode,
166 raw_ostream &O) {
167 assert(OpNum + 1 < MI->getNumOperands() && "Insufficient operands");
168 const MachineOperand &BaseMO = MI->getOperand(i: OpNum);
169 const MachineOperand &OffsetMO = MI->getOperand(i: OpNum + 1);
170 assert(BaseMO.isReg() && "Unexpected base pointer for inline asm memory operand.");
171 assert(OffsetMO.isImm() && "Unexpected offset for inline asm memory operand.");
172 int Offset = OffsetMO.getImm();
173
174 if (ExtraCode)
175 return true; // Unknown modifier.
176
177 if (Offset < 0)
178 O << "(" << BPFInstPrinter::getRegisterName(Reg: BaseMO.getReg()) << " - " << -Offset << ")";
179 else
180 O << "(" << BPFInstPrinter::getRegisterName(Reg: BaseMO.getReg()) << " + " << Offset << ")";
181
182 return false;
183}
184
185void BPFAsmPrinter::emitInstruction(const MachineInstr *MI) {
186 if (MI->isCall()) {
187 for (const MachineOperand &Op : MI->operands()) {
188 if (Op.isGlobal()) {
189 if (const GlobalValue *GV = Op.getGlobal())
190 if (GV->getName() == BPF_TRAP)
191 SawTrapCall = true;
192 }
193 }
194 }
195
196 BPF_MC::verifyInstructionPredicates(Opcode: MI->getOpcode(),
197 Features: getSubtargetInfo().getFeatureBits());
198
199 MCInst TmpInst;
200
201 if (!BTF || !BTF->InstLower(MI, OutMI&: TmpInst)) {
202 BPFMCInstLower MCInstLowering(OutContext, *this);
203 MCInstLowering.Lower(MI, OutMI&: TmpInst);
204 }
205 EmitToStreamer(S&: *OutStreamer, Inst: TmpInst);
206}
207
208void BPFAsmPrinter::emitFunctionBodyEnd() {
209 // Emit .bpf_cleanup section with a flat table of
210 // (call_site, landing_pad) pairs.
211 const std::vector<LandingPadInfo> &LandingPads = MF->getLandingPads();
212 if (LandingPads.empty())
213 return;
214
215 MCContext &Ctx = OutContext;
216 auto *CleanupSec =
217 Ctx.getELFSection(Section: ".bpf_cleanup", Type: ELF::SHT_PROGBITS, Flags: ELF::SHF_ALLOC);
218 OutStreamer->switchSection(Section: CleanupSec);
219
220 const auto &TypeInfos = MF->getTypeInfos();
221 const Function &F = MF->getFunction();
222 LLVMContext &LLVMCtx = F.getContext();
223
224 // Each landing pad has BeginLabels/EndLabels marking the invoke
225 // call sites that unwind to it.
226 for (const LandingPadInfo &LP : LandingPads) {
227 // BPF treats all landing pads as catch-all: the kernel redirects to
228 // the landing pad regardless of exception type. Reject type-specific
229 // catches and filters which would silently misbehave.
230 for (int TId : LP.TypeIds) {
231 if (TId > 0 && TypeInfos[TId - 1] != nullptr) {
232 LLVMCtx.diagnose(DI: DiagnosticInfoUnsupported(
233 F, "BPF does not support type-specific exception catches yet"));
234 return;
235 }
236 if (TId < 0) {
237 LLVMCtx.diagnose(DI: DiagnosticInfoUnsupported(
238 F, "BPF does not support exception filters yet"));
239 return;
240 }
241 }
242
243 MCSymbol *LPLabel = LP.LandingPadLabel;
244 if (!LPLabel)
245 continue;
246 for (unsigned i = 0, e = LP.BeginLabels.size(); i != e; ++i) {
247 MCSymbol *Begin = LP.BeginLabels[i];
248 MCSymbol *End = LP.EndLabels[i];
249
250 // Each entry is 3 x 4 bytes: begin, end, landing_pad.
251 // The invoke region [begin, end) may include argument setup
252 // before the call. The runtime checks begin <= PC < end.
253 OutStreamer->emitSymbolValue(Sym: Begin, Size: 4);
254 OutStreamer->emitSymbolValue(Sym: End, Size: 4);
255 OutStreamer->emitSymbolValue(Sym: LPLabel, Size: 4);
256 }
257 }
258
259 // Switch back to the function's section.
260 OutStreamer->switchSection(Section: MF->getSection());
261}
262
263MCSymbol *BPFAsmPrinter::getJTPublicSymbol(unsigned JTI) {
264 SmallString<60> Name;
265 raw_svector_ostream(Name)
266 << "BPF.JT." << MF->getFunctionNumber() << '.' << JTI;
267 MCSymbol *S = OutContext.getOrCreateSymbol(Name);
268 if (auto *ES = static_cast<MCSymbolELF *>(S)) {
269 ES->setBinding(ELF::STB_GLOBAL);
270 ES->setType(ELF::STT_OBJECT);
271 }
272 return S;
273}
274
275void BPFAsmPrinter::emitJumpTableInfo() {
276 const MachineJumpTableInfo *MJTI = MF->getJumpTableInfo();
277 if (!MJTI)
278 return;
279
280 const std::vector<MachineJumpTableEntry> &JT = MJTI->getJumpTables();
281 if (JT.empty())
282 return;
283
284 const TargetLoweringObjectFile &TLOF = getObjFileLowering();
285 const Function &F = MF->getFunction();
286
287 MCSection *Sec = OutStreamer->getCurrentSectionOnly();
288 MCSymbol *SecStart = Sec->getBeginSymbol();
289
290 MCSection *JTS = TLOF.getSectionForJumpTable(F, TM);
291 assert(MJTI->getEntryKind() == MachineJumpTableInfo::EK_BlockAddress);
292 unsigned EntrySize = MJTI->getEntrySize(TD: getDataLayout());
293 OutStreamer->switchSection(Section: JTS);
294 for (unsigned JTI = 0; JTI < JT.size(); JTI++) {
295 ArrayRef<MachineBasicBlock *> JTBBs = JT[JTI].MBBs;
296 if (JTBBs.empty())
297 continue;
298
299 MCSymbol *JTStart = getJTPublicSymbol(JTI);
300 OutStreamer->emitLabel(Symbol: JTStart);
301 for (const MachineBasicBlock *MBB : JTBBs) {
302 const MCExpr *Diff = MCBinaryExpr::createSub(
303 LHS: MCSymbolRefExpr::create(Symbol: MBB->getSymbol(), Ctx&: OutContext),
304 RHS: MCSymbolRefExpr::create(Symbol: SecStart, Ctx&: OutContext), Ctx&: OutContext);
305 OutStreamer->emitValue(Value: Diff, Size: EntrySize);
306 }
307 const MCExpr *JTSize =
308 MCConstantExpr::create(Value: JTBBs.size() * EntrySize, Ctx&: OutContext);
309 OutStreamer->emitELFSize(Symbol: JTStart, Value: JTSize);
310 }
311}
312
313char BPFAsmPrinter::ID = 0;
314
315INITIALIZE_PASS(BPFAsmPrinter, "bpf-asm-printer", "BPF Assembly Printer", false,
316 false)
317
318// Force static initialization.
319extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void
320LLVMInitializeBPFAsmPrinter() {
321 RegisterAsmPrinter<BPFAsmPrinter> X(getTheBPFleTarget());
322 RegisterAsmPrinter<BPFAsmPrinter> Y(getTheBPFbeTarget());
323 RegisterAsmPrinter<BPFAsmPrinter> Z(getTheBPFTarget());
324}
325
326PreservedAnalyses BPFAsmPrinterBeginPass::run(Module &M,
327 ModuleAnalysisManager &MAM) {
328 BPFAsmPrinter &AsmPrinter = static_cast<BPFAsmPrinter &>(
329 MAM.getResult<AsmPrinterAnalysis>(IR&: M).getPrinter());
330 setupModuleAsmPrinter(M, MAM, AsmPrinter);
331 AsmPrinter.doInitialization(M);
332 return PreservedAnalyses::all();
333}
334
335PreservedAnalyses BPFAsmPrinterPass::run(MachineFunction &MF,
336 MachineFunctionAnalysisManager &MFAM) {
337 BPFAsmPrinter &AsmPrinter = static_cast<BPFAsmPrinter &>(
338 MFAM.getResult<ModuleAnalysisManagerMachineFunctionProxy>(IR&: MF)
339 .getCachedResult<AsmPrinterAnalysis>(IR&: *MF.getFunction().getParent())
340 ->getPrinter());
341 setupMachineFunctionAsmPrinter(MFAM, MF, AsmPrinter);
342 AsmPrinter.runOnMachineFunction(MF);
343 return PreservedAnalyses::all();
344}
345
346PreservedAnalyses BPFAsmPrinterEndPass::run(Module &M,
347 ModuleAnalysisManager &MAM) {
348 BPFAsmPrinter &AsmPrinter = static_cast<BPFAsmPrinter &>(
349 MAM.getResult<AsmPrinterAnalysis>(IR&: M).getPrinter());
350 setupModuleAsmPrinter(M, MAM, AsmPrinter);
351 AsmPrinter.doFinalization(M);
352 return PreservedAnalyses::all();
353}
354