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
, MipsAsmPrinter
emitFunctionBodyStart()/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::EmitJal
MipsAsmPrinter::EmitInstrReg
MipsAsmPrinter::EmitInstrRegReg
All 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?