Asm Printer

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.

  • target-specific subclasses of AsmPrinter, such as SparcAsmPrinter, MipsAsmPrinter
  • target specific functions such as emitFunctionBodyStart()/End()

Additionally, for other code generation other than MachineFunction labels:

  • the .td files used to generate instruction printer automatically.
    • add $dst, $src, $src2
    • Need routines to print operands. Where???
  • <target>MCInstLower.cpp: code that lowers a MachineInstr to an MCInst
    • often target specific
    • is responsible for turning jump table entries, constant pool indicies, global variable address, etc, into MCLabels as appropriate.
    • is responsible for expanding pseudo ops used by the code generator into the actual machine instructions they corresponding to.
    • The MCInst that are generated by this are fed into the instructtion printer or the encoder.
  • the TargetLoweringObjectFile class

Tracking Pass MipsAsmPrinter

MipsAsmPrinter::runOnMachineFunction(MachineFunction &MF)

The pass is registered using struct RegisterAsmPrinter:

code for pass registration

The pass is added to the pass manager in LLVMTargetMachine::addAsmPrinter():

tracking `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:

tracking `MipsAsmPrinter::runOnMachineFunction()`

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).

  • Set the current function symbol, begin, exception symbol, etc.
  • If has EmitStackSizeSection option, create and set func begin symbol: CurrentFnBegin = createTempSymbol("func_begin").
  • Get result from another pass: MachineOptimizationRemarkEmitterPass.
    code AsmPrinter::SetupMachineFunction()

EmitFunctionBody(), this emits the body and trailer for a function.

Steps:

  1. EmitFunctionHeader();
  2. EmitFunctionBodyStart(); -> virtual -> MipsAsmPrinter::EmitFunctionBodyStart()
  3. Core Part: Scan every Basic Block, MBB:
    • EmitBasicBlockStart(MBB)
    • For Every Instruction, MI:
      • check and emit labels/comments/debuglocs if applicable
      • Check 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);
      • default case: EmitInstruction(&MI);
      • if exists post-instructino symbol: OutStreamer->EmitLable(S)
    • EmitBasicBlockEnd(MBB)
  4. if has .subsections_via_symbols, emit a noop to avoid labels collapsing together.
  5. get IR function F, and scan every BB, if BB.hasAddressTaken(), do the following:
    • GetBlockAddressSymbol(&BB)
    • If symbol not defined, emit it. // block address removed by CodeGen???
    • OutStreamer->AddComment("Address of block that was removed by CodeGen");
    • OutStreamer->EmitLabel(Sym)
  6. EmitFunctionBodyEnd()
  7. If target wants a .size directive for the size of function, emit it
    • OutStreamer->emitELFSize(CurrentFnSym, SizeExp)
  8. EmitJumpTableInfo()
  9. EmitStackSizeSection(*MF)
  10. OutStreamer->AddBlankLine()
simplified code `AsmPrinter::EmitFunctionBody()`

AsmPrinter::EmitInstruction()

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).

Before Instruction Emission

Instruction Emission

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.

tracking `MipsAsmPrinter::EmitInstruction()`

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()

A class hierachy for the streamers

Actual emission will be done by the actual target implementation.

See MCStreamers for more details.

Tracking TextSection

Question: Find out where is the TextSection being filled with instructions.

Tracking places where current section is switched to the TextSection:

  1. in the end of MipsASMPrinter::EmitStartOfAsmFile(Module &M)
    • EmitStartOfAsmFile() is called in parent class AsmPrinter::doInitialization(Module &M)
  2. in the end of 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

AsmPrinter::emitGlobalVariable

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(..);

Tracking DataSection

Question: find out where is the DataSection being filled with data values.

See subpage Global Print

Tracking StackSizeSection

AsmPrinter::EmitStackSizeSection(), got called in AsmPrinter::EmitFunctionBody() (as above).

Steps:

  1. Check command line option, if no option, return:
    • if (!MF.getTarget().Options.EmitStackSizeSection) return;
  2. Get StackSizeSection (instance of MCSection) from TargetLoweringObjectFile
    • MCSection *StackSizeSection = getObjFileLowering().getStackSizesSection(*getCurrentSection());
  3. Switch current section to StackSizeSection.
    • OutStreamer->PushSection();
    • OutStreamer->SwitchSection(StackSizeSection);
  4. Get StackSize from MachineFrameInfo of current MachineFunction
    • uint64_t StackSize = FrameInfo.getStackSize();
  5. Emit the function symbol, along with StackSize
    • OutStreamer->EmitSymbolValue(FunctionSymbol, TM.getProgramPointerSize());
    • OutStreamer->EmitULEB128IntValue(StackSize);
  6. 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

  • Emit Globals in AsmPrinter
  • 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 .

Created Jul 18, 2020 // Last Updated May 18, 2021

If you could revise
the fundmental principles of
computer system design
to improve security...

... what would you change?