Reference reference
llvm/include/llvm/CodeGen/AsmPrinter.h
llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
AsmPrinter is a pass at MachineFunction level: class AsmPrinter : public MachineFunctionPass.
It acts as a driver for the MC layer streamers. It defines the MachineFunction pass that calls OutStreamer->emitXXX() to emit all the stuff to the binary or assembly files.
The (misnamed) target-independent AsmPrinter class implements the general lowering process converting MachineFunction’s into MC label constructs.
AsmPrinter, such as SparcAsmPrinter, MipsAsmPrinteremitFunctionBodyStart()/End()Additionally, for other code generation other than MachineFunction labels:
.td files used to generate instruction printer automatically.
add $dst, $src, $src2<target>MCInstLower.cpp: code that lowers a MachineInstr to an MCInst
TargetLoweringObjectFile classMipsAsmPrinter::runOnMachineFunction(MachineFunction &MF)
The pass is registered using struct RegisterAsmPrinter:
// llvm/lib/Target/Mips/MipsAsmPrinter.cpp
extern "C" void LLVMInitializeMipsAsmPrinter() {
RegisterAsmPrinter<MipsAsmPrinter> X(getTheMipsTarget());
RegisterAsmPrinter<MipsAsmPrinter> Y(getTheMipselTarget());
RegisterAsmPrinter<MipsAsmPrinter> A(getTheMips64Target());
RegisterAsmPrinter<MipsAsmPrinter> B(getTheMips64elTarget());
RegisterAsmPrinter<MipsAsmPrinter> C(getTheMipsCheriTarget());
}
The pass is added to the pass manager in LLVMTargetMachine::addAsmPrinter():
// llvm/lib/CodeGen/LLVMTargetMachine.cpp
bool LLVMTargetMachine::addAsmPrinter(PassManagerBase &PM,
raw_pwrite_stream &Out,
raw_pwrite_stream *DwoOut,
CodeGenFileType FileType,
MCContext &Context) {
// ...
// Create the AsmPrinter, which takes ownership of AsmStreamer if successful.
FunctionPass *Printer =
getTarget().createAsmPrinter(*this, std::move(AsmStreamer));
errs()<<"Lele: asm printer pass added\n";
if (!Printer)
return true;
PM.add(Printer);
return false;
}
// The call path for this function is:
clang::EmitBackendOutput()
-> EmitAssemblyHelper::EmitAssembly()
-> EmitAssemblyHelper::AddEmitPasses()
-> LLVMTargetMachine::addPassesToEmitFile()
-> LLVMTargetMachine::addAsmPrinter()
runOnMachineFunction is implemented in both child class, the target-specific AsmPrinter (e.g. MipsAsmPrinter), as well as the parent class, target independent AsmPrinter.
The Mips printer will call its parent for target independent prints (SetupMachineFunction(), EmitFunctionBody(), etc.).
The Mips specific pass:
// llvm/lib/Target/Mips/MipsAsmPrinter.cpp
bool MipsAsmPrinter::runOnMachineFunction(MachineFunction &MF) {
Subtarget = &MF.getSubtarget<MipsSubtarget>();
MipsFI = MF.getInfo<MipsFunctionInfo>();
if (Subtarget->inMips16Mode())
for (std::map<
const char *,
const Mips16HardFloatInfo::FuncSignature *>::const_iterator
it = MipsFI->StubsNeeded.begin();
it != MipsFI->StubsNeeded.end(); ++it) {
const char *Symbol = it->first;
const Mips16HardFloatInfo::FuncSignature *Signature = it->second;
if (StubsNeeded.find(Symbol) == StubsNeeded.end())
StubsNeeded[Symbol] = Signature;
}
MCP = MF.getConstantPool();
// In NaCl, all indirect jump targets must be aligned to bundle size.
if (Subtarget->isTargetNaCl())
NaClAlignIndirectJumpTargets(MF);
AsmPrinter::runOnMachineFunction(MF);
emitXRayTable();
return true;
}
The parent’s pass entry (for target independent emission):
// llvm/include/llvm/CodeGen/AsmPrinter.h
/// Emit the specified function out to the OutStreamer.
bool runOnMachineFunction(MachineFunction &MF) override {
SetupMachineFunction(MF);
EmitFunctionBody();
return false;
}
SetupMachineFunction(MF) (Target independent).
EmitStackSizeSection option, create and set func begin symbol: CurrentFnBegin = createTempSymbol("func_begin").// llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
void AsmPrinter::SetupMachineFunction(MachineFunction &MF) {
this->MF = &MF;
// Get the function symbol.
CurrentFnSym = getSymbol(&MF.getFunction());
CurrentFnSymForSize = CurrentFnSym;
CurrentFnBegin = nullptr;
CurExceptionSym = nullptr;
bool NeedsLocalForSize = MAI->needsLocalForSize();
if (needFuncLabelsForEHOrDebugInfo(MF, MMI) || NeedsLocalForSize ||
MF.getTarget().Options.EmitStackSizeSection) {
CurrentFnBegin = createTempSymbol("func_begin");
if (NeedsLocalForSize)
CurrentFnSymForSize = CurrentFnBegin;
}
ORE = &getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE();
}
EmitFunctionBody(), this emits the body and trailer for a function.
Steps:
MBB:
EmitBasicBlockStart(MBB)MI:
Mi.getOpcode() and handle different opcodes:CFI_INSTRUCTION: emitCFIInstruction(MI);LOCAL_ESCAPE: emitFrameAlloc(MI);ANNOTATION_LABEL, EH_LABEL, GC_LABEL: OutStreamer->EmitLabel(MI.getOperand(0).getMCSymbol());INLINEASM, INLINEASM_BR: EmitInlineAsm(&MI);EmitInstruction(&MI);OutStreamer->EmitLable(S)EmitBasicBlockEnd(MBB).subsections_via_symbols, emit a noop to avoid labels collapsing together.F, and scan every BB, if BB.hasAddressTaken(), do the following:
GetBlockAddressSymbol(&BB)OutStreamer->AddComment("Address of block that was removed by CodeGen");OutStreamer->EmitLabel(Sym)EmitFunctionBodyEnd()OutStreamer->emitELFSize(CurrentFnSym, SizeExp)EmitJumpTableInfo()EmitStackSizeSection(*MF)OutStreamer->AddBlankLine()// llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
/// EmitFunctionBody - This method emits the body and trailer for a
/// function.
void AsmPrinter::EmitFunctionBody() {
EmitFunctionHeader();
EmitFunctionBodyStart();
...
// Print out code for the function.
bool HasAnyRealCode = false;
int NumInstsInFunction = 0;
for (auto &MBB : *MF) {
// Print a label for the basic block.
EmitBasicBlockStart(MBB);
for (auto &MI : MBB) {
// Print the assembly for the instruction.
// If there is a pre-instruction symbol, emit a label for it here.
if (MCSymbol *S = MI.getPreInstrSymbol())
OutStreamer->EmitLabel(S);
...
switch (MI.getOpcode()) {
...
case TargetOpcode::INLINEASM:
case TargetOpcode::INLINEASM_BR:
EmitInlineAsm(&MI);
break;
...
default:
EmitInstruction(&MI);
break;
}
...
}
EmitBasicBlockEnd(MBB);
}
// Emit target-specific gunk after the function body.
EmitFunctionBodyEnd();
// Print out jump tables referenced by the function.
EmitJumpTableInfo();
// Emit section containing stack size metadata.
emitStackSizeSection(*MF);
...
}
There is no target independent instruction emission in AsmPrinter::EmitInstruction(), which is a virtual method requires all derived class (such as MipsAsmPrinter), to override this function.
Here we show the target dependent implemetations to emit instructions in MipsAsmPrinter::EmitInstruction();
We will see many target-independent routines from the MCStreamer derived classes are called to emit the instruction (e.g. emit a MCInst).
see code in llvm/lib/Target/Mips/MipsAsmPrinter.cpp
MipsAsmPrinter::EmitInstruction(), called by its parent class AsmPrinter::EmitFunctionBody()
Steps:
MipsAsmPrinter::EmitInstruction() acts as a driver to emit an instruction. It will finally calls the MCObjectStreamer to write object (e.g. .o) files or MCAsmStreamer to write assembly (e.g .s) files.
// llvm/lib/Target/Mips/MipsAsmPrinter.cpp
void MipsAsmPrinter::EmitInstruction(const MachineInstr *MI) {
MipsTargetStreamer &TS = getTargetStreamer();
unsigned Opc = MI->getOpcode();
TS.forbidModuleDirective();
...
MachineBasicBlock::const_instr_iterator I = MI->getIterator();
MachineBasicBlock::const_instr_iterator E = MI->getParent()->instr_end();
do {
...
MCInst TmpInst0;
MCInstLowering.Lower(&*I, TmpInst0);
EmitToStreamer(*OutStreamer, TmpInst0);
} while ((++I != E) && I->isInsideBundle()); // Delay slot check
}
Other Instructino emission:
MipsAsmPrinter::EmitJalMipsAsmPrinter::EmitInstrRegMipsAsmPrinter::EmitInstrRegRegAll above functions finally call OutStreamer->EmitInstruction(MCInst I, MCSubtargetInfo STI)
OutStreamer is of type std::unique_ptr<MCStreamer> OutStreamer, which has basically two implementations: MCObjectStreamer for object file, and MCAsmStreamer for assembly file.
But both the implementations (in child class) will call the following (parent) method: MCStreamer::EmitInstruction()
// Inheritance relations
MCStreamer (contain an instance of `MCTargetStreamer` as member `TargetStreamer`)
-> MCAsmStreamer (final)
-> MCObjectStreamer
-> MCELFStreamer
-> MipsELFStreamer
-> MCWasmStreamer
// MCTargetStreamer for directives.
// llvm/include/llvm/MCStreamer.h
MCTargetStreamer (contain an instance `MCStreamer` as member `Streamer`)
-> ARMTargetStreamer // include/llvm/MCStreamer.h
-> MipsTargetStreamer // lib/Target/Mips/MipsTargetStreamer.h
-> MipsTargetAsmStreamer
-> MipsTargetELFStreamer
-> RISCVTargetStreamer // lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h
Actual emission will be done by the actual target implementation.
See MCStreamers for more details.
Question: Find out where is the TextSection being filled with instructions.
Tracking places where current section is switched to the TextSection:
MipsASMPrinter::EmitStartOfAsmFile(Module &M)
EmitStartOfAsmFile() is called in parent class AsmPrinter::doInitialization(Module &M)MipsAsmPrinter::EmitEndOfAsmFile(Module &M)
EmitEndOfAsmFile() is called in parent class AsmPrinter::doFinalization(Module &M)==> most of time, the text section is the current section; where another section is needed, use PushSection() -> SwitchSection -> do-your-emission -> PopSection(). Examples can be found below in Tracking StackSizeSection.
Tracking the pass where instruction is emitted, see above AsmPrinter::EmitInstruction(), and the instruction emission in MCStreamer
void AsmPrinter::emitGlobalVariable(const GlobalVariable *GV)
AsmPrinter::doFinalization(Module &M)
=> AsmPrinter::emitGlobalVariable(const GlobalVariable *GV)
=> MCSymbol *GVSym = getSymbol(GV); MCSymbol *EmittedSym = GVSym;
=> emitVisibility(EmittedSym, GV->getVisitbility(), !GV->isDeclaration());
=> OutStreamer->emitSymbolAttribute(Sym, Attr);
=> if (GVKind.isCommon()) OutStreamer->emitCommonSymbol(GVSym, Size,...);
// Determine to which section this global should be emitted.
=> MCSection *TheSection = getObjFileLowering().SectionForGlobal(GV, GVKind, TM);
=> MCSymbol *EmittedInitSym = GVSym;
=> OutStreamer->SwitchSection(TheSection);
=> emitLinkage(GV, EmittedInitSym);
=> emitAlignment(Alignment, GV);
=> OutStreamer->emitLabel(EmittedInitSym);
=> emitGlobalConstant(GV->getParent()->getDataLayout(), GV->getInitializer(),
static_cast<uint64_t>(TailPadding));
=> if (Size) emitGlobalConstantImpl(DL, CV, *this);
=> if 1/2/4/8 OutStreamer->emitIntValue(CI->getZExtValue(), Size);
=> if larger: emitGlobalConstantLargeInt(...)
==> OutStreamer->emitIntValue(Val, 8)
==> OutStreamer->emitIntValue(ExtraBits, Size)
=> OutStreamer->AddBlankLine()
=> AsmPrinter::emitGlobalGOTEquivs()
=> AsmPrinter::emitGlobalVariable(..);
Question: find out where is the DataSection being filled with data values.
See subpage Global Print
AsmPrinter::EmitStackSizeSection(), got called in AsmPrinter::EmitFunctionBody() (as above).
Steps:
if (!MF.getTarget().Options.EmitStackSizeSection) return;StackSizeSection (instance of MCSection) from TargetLoweringObjectFile
MCSection *StackSizeSection = getObjFileLowering().getStackSizesSection(*getCurrentSection());StackSizeSection.
OutStreamer->PushSection();OutStreamer->SwitchSection(StackSizeSection);StackSize from MachineFrameInfo of current MachineFunction
uint64_t StackSize = FrameInfo.getStackSize();StackSize
OutStreamer->EmitSymbolValue(FunctionSymbol, TM.getProgramPointerSize());OutStreamer->EmitULEB128IntValue(StackSize);Switch section back. OutStreamer->PopSection();
// llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
void AsmPrinter::emitStackSizeSection(const MachineFunction &MF) {
if (!MF.getTarget().Options.EmitStackSizeSection)
return;
MCSection *StackSizeSection =
getObjFileLowering().getStackSizesSection(*getCurrentSection());
if (!StackSizeSection)
return;
const MachineFrameInfo &FrameInfo = MF.getFrameInfo();
// Don't emit functions with dynamic stack allocations.
if (FrameInfo.hasVarSizedObjects())
return;
OutStreamer->PushSection();
OutStreamer->SwitchSection(StackSizeSection);
const MCSymbol *FunctionSymbol = getFunctionBegin();
uint64_t StackSize = FrameInfo.getStackSize();
OutStreamer->EmitSymbolValue(FunctionSymbol, TM.getProgramPointerSize());
OutStreamer->EmitULEB128IntValue(StackSize);
OutStreamer->PopSection();
}
Caller path:
AsmPrinter::EmitFunctionBody() -> AsmPrinter::emitStackSizeSection
Reference llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp The call path of emitting a global variable: AsmPrinter::doFinalization(Module &) => emitGlobalVariable(&G); // for (const auto &G : M.globals()) => emitGlobalGOTEquivs() => emitGlobalVariable(GV) // for (auto *GV: FailedCandidates) Function interfaces for global emission in AsmPrinter: // llvm/include/llvm/CodeGen/AsmPrinter.h /// This class is intended to be used as a driving class for all asm writers. classAsmPrinter : public MachineFunctionPass { ... /// Emit the specified global variable to the .
If you could revise
the fundmental principles of
computer system design
to improve security...
... what would you change?