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